expressões retornam valores.
comandos principalmente causam efeitos colaterias/mudanca de estado
expressões
3 + x + 4.5*sqrt(y)
comandos
x=4
if x==4 :
---
else:
---
print(x)
(normalmente) comandos são “executados/executed/run” e expressões são “avaliadas/evaluated”
Em linguagens funcionais o programa é uma expressão
func1(func2(func3(x)))
mas mesmo assim existem comandos
para I/O e para atribuir valor a variaveis locais.
Declarações são comandos/informações/statements/pragma para o compilador/interpretador. Declaração de variavies, imports, macros em C, etc
quase todas as linguagens
func(arg1, arg2)
familia haskell (embora acho que comecou com APL), separado por brancos
func arg1 arg2
familia lisp
(func arg1 arg2)
A vantagem da familia tradicional é que é obvio como colocar expressões para os argumentos
func(x+45, g(y))
em haskell é bem mais chato
errado:
func x + 45 g y
são preciso parenteses
func (x + 45) (g y)
Em lisp, os parenteses sao usados necessariamente
(func (+ x 45) (g y))
tradicional - operadores infixos (no meio)
x + y * 4 / z**2
variações quanto a exponenciação (“^”)
precisa das prioridades na ordem de operações
*
e /
+
e -
lisp - operadores prefixo (no começo) e parenteses, como para a chamada da função
(+ x (* y * (/ 4 (pow z 2))))
vantagens na notação prefixa é que os operadores nao são mais binários
(+ 3 a b c d e f)
Algumas versões do comandos if
so aceita um comando como
a parte `then
um só comando para o else
(por
exemplo C). Nesse caso, se vc quer mais de um comando, vc precisa de um
bloco. Sintaticamente, um bloco faz o papel de um so comando.
blocos são delimitados por “{” e “}” (C)
blocos delimitados por begin
e end
(pascal)
Algumas linguagens entendem que há mais de um comando na parte
then
, e esses comandos terminam no else
. Mas
isso cria um problema que é quando os comandos do else
terminam? Bash por exemplo usa um terminador do if
, o
fi
.
Algumas ling usam indentação para indicar o bloco. O fim da indentação indica o fim do bloco. E a quantidade de indentação indica um bloco dentro do outro.
O problema do else (dangling else)
if x ==2 then
c1
c2
if y == 4 then
c3
c4
else
c5
c6
Esse else é do 1o ou do 2o if? Ou o 2o if é um if-then ou um if-then-else
em C isso é ainda um problema em um caso, onde nao há bloco
if (x==2) if (y==4) c4; else c5
nesse caso, o else é atribuido ao segundo if.
tradicional (C, python, etc)
if condicao ou if (condicao)
....
else
...
pascal, haskell
if condicao then
...
else
...
em haskell o if tem sempre um else (veremos isso no futuro)
familia C (rust,java, etc) “;”
mais moderno (python haskell, R, go, etc) mudança de linha
acho que todas ling mais modernas permitem mais de um comando por linha separados por “;”
familia C: primeiro valor tem indice 0 (python, java, go, rust)
familia fortran : primeiro valor tem indice 1 (R, julia)
x = 2 (C python etc)
x := 2 (pascal)
x <- 2 (R)
em python
o tamanho de uma lista
def tam(l):
if l==[]:
return 0
return 1 + tam(l[1:])
o maior de uma lista
def maior1(l):
if len(l) == 1:
return l[0]
aux = maior1(l[1:])
if l[0] >= aux :
return l[0]
else:
return aux
Essa solucao precisa da variavel local. sem ela o problema passa de tempo linear para exponencial
def maior2(l):
if len(l) == 1:
return l[0]
if l[0] >= maior2(l[1:]):
return l[0]
else:
return maior2(l[1:])
no pior caso vc faz a mesma recursao 2 vezes.
recursao no resto e depois vc faz o seu trabalho
def tam1(l):
if l==[]:
return 0
return 1+tam1(l[1:])
def tam1(l):
if l==[]:
return 0
aux = +tam1(l[1:])
return aux+1
o parametro extra é o trabalho da recursão feito antes desta chamada
def tamanho(l):
return tam2(l,0)
def tam2(l, x):
if l==[]:
return x
return tam(l[1:], x+1)
def maior(l):
return maior2(l[1:], l[0])
def maior2(l, m):
if l==[]:
return m
if l[0] > m:
return maior2(l[1:], l[0])
else:
return maior2(l[1:], m)
A recursao sem o parametro do trabalho anterior será chamada de fold right (porque o resultado vem da direita - fim da lista)
a recursao com o trabalho anterior será chamado de fold left (porque o resultado vem da esquerda - do começo da lista).
Fold left tem vantagens
A ultima coisa que vc faz é a chamada recursiva e portanto nada mais sera executado nessa chamada da recursao - as variaveis locais nao precisam ser lembradas. O compilador simplismente reusa o espaco na pilha para a proxima chamada recursiva.