Aula 2

Familias de sintaxe em linguagens de programação

expressões vs comandos vs declarações

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

chamada de funções

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))

Expressões matemáticas

tradicional - operadores infixos (no meio)

x + y * 4 / z**2

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)

Blocos e Ifs

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.

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.

sintaxe do 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)

separaçao entre comandos

acho que todas ling mais modernas permitem mais de um comando por linha separados por “;”

listas e arrays

Atribuiçao

x = 2 (C python etc)
x := 2 (pascal)
x <- 2 (R)

recursao revisao

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 com um parametro extra

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.