Aula 3

TROCOU O LINK PARA O LIVRO TEXTO

livro texto capitulos: 2 (tuplas, range, list comprehension) , 4 (where, let, case) e 5 (recursão)

haskell online

Sintaxe

Precedencia

a b c d + g h i

Blocos

maior a b = if a>b 
              then a 
              else b 

Guards

maior a b = if a > b 
       then a
       else b

maior' a b 
 | a > b = a
 | otherwise = b

Da notação matemática, mas com a condição antes do valor

f(a,b)=\left\{ \begin{array}{l l} a, & {if}\ a>b \\ b, & {otherwise} \end{array}\right.

Variáveis e funções locais

posicao it [] = 0
posicao it (x:xs) 
  | it == x = 1
  | otherwise = if (posicao it xs) == 0 then 0 else (posicao it xs) + 1  
  --- dupla recursao a nao ser que (posicao it xs) == 0

where - define as variáveis (e funções) locais depois da chamada “principal”

maior [x] = x
maior (x:xs) = if x>mm then x else mm
    where mm = maior xs

let … in - define as variáveis e funções locais antes da chamada principal

maior [x] = x
maior (x:xs) = let
         mm = maior xs
     in if x>mm then x else mm 

where permite continuar usando guards

maior [x] = x
maior (x:xs) 
   | x>mm = x
   | otherwise = mm
   where mm = maior xs

Recursão com acumulador

As funções recursivas até agora são do tipo - recursao no resto da lista e depois computa a solução com a cabeça da lista

soma [] = 0
soma (x:xs) = x + (soma xs)

A ideia do acumulador é fazer o calculo usando o head e o acumulador recebido e passar o acumulador alterado para a recursão.

O acumulador acumula o trabalho das instancias que vieram antes da recursão

--  soma' l acc
soma' [] acc = acc   -- caso base sempre retorna o acumulador
soma' (x:xs) acc = soma' xs (acc+x) 
-- caso recursivo faz primeiro a conta (acc+x) e so no final chama a recursao

Mas a primeira chamada funçao soma' precisa setar o acumulador da forma certa. A funçao soma' é uma função local de soma

soma l = soma' l 0
  where soma' [] acc = acc
        soma' (x:xs) acc = soma' xs (x+acc)

caso do reverte

reverte [] = []
reverte (x:xs) = (reverte xs) ++ [x]

Esse codigo é quadratico pois o ++ passeia pela primeira lista “até achar o final” para grudar a segunda lista (se a lista é implementada como uma lista ligada)

T(n) = T(n-1)+n+c
==> T(n) = O(n^2)

neste caso, recursão com acumulador torna a função linear

reverte l = reverte' l []
   where 
     reverte' [] acc = acc
     reverte' (x:xs) acc = reverte' xs (x:acc)

List Comprehension

[ f x | x <- fonte, condicao1, condicao2]
[ x+10 | x <- [1..10], x `mod` 2 == 0]

[1..10] é um range - gera uma lista de 1 a 10

[2,5..30] gera a lista [2,5,8,11,14,17,20,23,26,29]

somapares xs = soma [x | x <- xs, x `mod` 2 == 0]

Tupla

Como a tupla do Python. Mas funcoes de listas nao funcionam em tuplas

head ("Jose" , 47)

Pattern matching funciona em tuplas

somaAno (n,id) = (n, id+1)

Para tuplas de 2 elementos:

fst ("jose",3)
==> "jose"

fst (a,b) = a

snd ("jose",3)
==> 3

snd (a,b) = b

Retornar uma tupla é o jeito para uma função retornar mais de uma coisa

trocaultimo

trocaultimo velho novo lista = fst (trocaultimo' velho novo lista)

trocaultimo' velho novo []  = ([],False)
trocaultimo' velho novo (x:xs) = let
             (xxs,trocou) = trocaultimo' velho novo xs
          in if trocou || (x /= velho)  then (x:xxs, trocou)
                                        else (novo:xxs, True)

trocaultimo retorna uma tupla com a lista trocada e um booleano se ele trocou ou nao o velho pelo novo.

Mais simples:

trocaultimo n v l = reverte (troca1 n v (reverte l))

Exercícios

refaça os exercicios da aula passada usando variaveis locais, recursão com acumulador, list comprehension, funções retornando tuplas, combinando funções ja implementadas, etc se for o caso. Em particular tente usar acumuladores para aprender a pensar dessa forma.

Alem disso faça:

split "qwertyuiopoiuyt" 't' ==> ["qwer", "yuiopoiuyt"]
splitall "qwertyuiopoiuytxxt" 't' ==> ["qwer", "yuiopoiuy", "xx", ""]  ou  ["qwer", "yuiopoiuy", "xx"]