Programação Funcional Lucília Camarão de Figueiredo Universidade Federal de Ouro Preto [email protected] Aula 04: Definição de funções 1 D EFINIÇÃO DE FUNÇÕES f x1 x2 ...xn = E Define uma função f de tipo T1 → T2 → ...→ Tn → T, onde Ti é o tipo do i-ésimo argumento de f, para i=1,. . . .n, e T é o tipo da expressão E (ou seja, o tipo do valor retornado por f) Exemplos de operações: double :: Num a => a → a double x = 2*x exOr :: Bool → Bool → Bool exOr x y = (x || y) && (not (x && y)) max3 x y z :: Ord a => a → a → a → a max3 x y z = max(max(x,y),z) D EFINIÇÃO DE FUNÇÕES 2 E XPRESSÕES C ONDICIONAIS f x1 x2 ...xn = E Na maioria das linguagens de programação, funções podem ser definidas usando-se expressões condicionais: max :: Ord a => a → a → a max x y = if (x >= y) then x else y abs :: Num a, Ord a => a → a abs x = if x > 0 then x else -x E XPRESSÕES C ONDICIONAIS 3 E XPRESSÕES C ONDICIONAIS Expressões condicionais podem ser aninhadas: signum x = if x>0 then 1 else if x==0 then 0 else -1 Nota: Em Haskell, expressões condicionais devem sempre ter a cláusula else — isso evita possíveis problemas de ambiguidade envolvendo expressões condicionais aninhadas. E XPRESSÕES C ONDICIONAIS 4 E QUAÇÕES COM G UARDAS Como alternativa ao uso de expressões condicionais, podemos definir funções usando equações com guardas: abs x | x > 0 = x | otherwise = -x signum x | x>0 = 1 | x==0 = 0 | x<0 = -1 Equações com guardas tornam as definições mais legíveis. Nota: A condição otherwise usada em equações com guardas é definida no Prelude como otherwise = True. E QUAÇÕES COM G UARDAS 5 C ASAMENTO DE PADRÃO O uso casamento de padrão favorece a legibilidade: not :: Bool → Bool not True = False not False = True (&&) :: True && True && False && False && C ASAMENTO DE PADRÃO Bool → True = False = True = False = Bool → Bool True False False False 6 C ASAMENTO DE PADRÃO O operador && pode ser definido mais facilmente como: True && True = True _ && _ = False Entretanto, a seguinte definição é sempre mais eficiente (uma vez que sempre evita a avaliação do segundo argumento): False && _ True && b = False = b Nota: O padrão _ corresponde a um "coringa", que casa com qualquer argumento. C ASAMENTO DE PADRÃO 7 PADRÕES PARA L ISTAS Em Haskell, toda lista não vazia é construída por meo do operador : (denominado cons), que adiciona um novo elemento ao início da lista, ou seja, : tem tipo a → [a] → [a]: [1,2,3] significa 1:(2:(3:[])) O operador cons também pode ser usado em padrões, caso no qual ele destrói uma lista não vazia: head :: a → [a] head (x:_) = x tail :: [a] → [a] tail (_:xs) = xs PADRÕES PARA L ISTAS 8 PADRÕES PARA L ISTAS Mais alguns exemplos de definições de funções sobre listas: length :: [a] → Int length [] = 0 length (_:xs) = 1 + length xs sum :: [a] → Int sum [] = 0 sum (x:xs) = x + length xs (++) :: [a] → [a] → [a] [] ++ ys = ys (x:xs) ++ ys = x:(xs++ys) PADRÕES PARA L ISTAS 9 PADRÕES – MAIS UM EXEMPLO : A função a seguir determina a média de uma lista de valores inteiros: average :: [Int] → Float average xs@(y:yx) = fromInt (sum xs) / fromInt (length xs) Essa definição ilustra o uso de alias em um padrão. A definição acima não é, entretanto, eficiente. Porque? Uma definição eficiente será apresentada mais adiante. PADRÕES – MAIS UM EXEMPLO : 10 E XPRESSÕES L AMBDA Podemos construir uma função sem que seja necessário dar um nome a essa função — para isso, usamos uma lambda abstração: λx → x+1 representa a função que, dado um argumento x, retorna x+1. E XPRESSÕES L AMBDA 11 P ORQUE E XPRESSÕES L AMBDA SÃO ÚTEIS ? • Expressões Lambda podem ser usadas para dar significado formal a definições de funções currificadas. Por exemplo: add x y = x+y significa add = λx → (λx → x+y) • Expressões Lambda são úteis para definir funções que retornam funções como resultado. Por exemplo: compose f g x = f (g x) é mais naturalmente definida como: compose f g = λx → f (g x) P ORQUE E XPRESSÕES L AMBDA SÃO ÚTEIS ? 12 E XERCÍCIOS 1. Dê 2 possíveis definições para o operador || usando casamento de padrão. 2. Considere uma função safeTail, que se comporta como a função tail, exceto que safeTail mapea a lista vazia em lista vazia, enquanto tail produz um erro nesse caso. Defina safeTail usando: (a) uma expressão condicional (b) equações com guardas (c) casamento de padrão Obs: A função null :: [a] → Bool, definida no Prelude, pode ser usada para testar se uma lista é vazia. Como vocx̂e definiria essa função? E XERCÍCIOS 13