Haskell/Lambdas e operadores

Fonte: testwiki
Revisão em 16h36min de 16 de abril de 2020 por imported>DannyS712 (<source> -> <syntaxhighlight> (phab:T237267))
(dif) ← Revisão anterior | Revisão atual (dif) | Revisão seguinte → (dif)
Saltar para a navegação Saltar para a pesquisa

Predefinição:Em tradução Predefinição:HaskellÍndice

Em Haskell existe um tipo de expressão extremamente importante chamada expressões lambdas, comumente chamadas de funções anônimas, as quais vamos introduzir neste módulo. Além disso, aprenderemos um pouco sobre operadores e notação infixa.

Expressões lambdas

Relembrando os exemplos do capítulo Listas II, suponha que queiramos dobrar todos os elementos de uma lista. Uma possível função para este trabalho seria:

dobrarLista ls = map dobro ls
    where dobro x = 2 * x

Ou ainda:

dobrarLista ls = let dobro x = 2 * x
                 in map dobro ls

Outra solução, porém, seria usar uma expressão lambda para representar a função auxilar dobro sem precisar usar let ou where:

dobrarLista ls = map (\x -> 2 * x) ls

Antes de entendermos a notação, precisamos fazer uma breve correlação entre Matemática e Computação.

Na Matemática, a função que dobra seu argumento pode ser representada por f(x)=2x. Com isso, ela passa a se chamar "f", sendo que poderia ter sido qualquer outro nome, como "g" ou "dobro".

Já nas Ciências da Computação existe um área de estudo chamada Cálculo lambda. É a partir dela que muitas linguagens de programação funcional são desenvolvidas, inclusive Haskell. O Cálculo lambda, por sua vez, não exige que as funções tenham nomes, e por isso elas podem ser chamadas de funções anônimas. Toda função é denotada por λ seguido das variáveis de entrada, um sinal de gráfico e a expressão matemática da função. A função f(x) pode se escrita como:

  1. λx2x, ou;
  2. λx.2x.

A notação usada em Haskell se assemelha com a primeira opção:

  1. \ é usada para representar o λ;
  2. -> substitui a seta .

A aplicação de funções anônimas também é semelhante em Haskell e no Cálculo lambda:

  • No Cálculo lambda, (λx2x)7 resulta em 14.
  • Em Haskell, (\x -> 2 * x) 7 resulta em 14.

Portanto, expressões lambdas funcionam como qualquer outras funções, mas, por não possuirem um nome, não podemos chamá-las em diferentes lugares do código. Isso as torna "descartáveis", o que é bastante útil quando precisamos usar certa função apenas uma vez como em map ou alguma fold, por exemplo.

Há um jeito, porém, de dar nomes às funções anônimas. Basta declarar uma variável que seja a função anônima. Por exemplo:

pitagoras = \x y -> sqrt (x^2 + y^2)

Acabamos de criar a função pitagoras a partir de uma expressão lambda que possui dois argumentos. Podemos testar no GHCi:

Predefinição:HaskellGHCi

Predefinição:Exercícios

Operadores

Em Haskell, um operador é qualquer função de dois argumentos que possuir um nome que consista apenas de caracteres não-alfanuméricos. Os operadores mais comuns são os aritméticos, como a adição, (+), a subtração, (-). Diferente de outras funções, que usam a notação de prefixos, operadores são infixos, isto é, são escritos entre os argumento, como 2 + 3 e 5 * 7. Mesmo assim, eles ainda podem ser usados com a notação de prefixos, bastando para isso envolvê-los entre parênteses:

Predefinição:HaskellGHCi

Novos operadores podem ser definidos como quaisquer outras funções, basta não usar caracteres alfanuméricos em seus nomes e colocando parênteses na assinatura de tipo. Por exemplo, vejamos a definição da função (\\) do módulo Data.List, a qual elimina os elementos iguais de duas listas:

(\\) :: (Eq a) => [a] -> [a] -> [a]
xs \\ ys = foldl (\zs y -> delete y zs) xs ys

A segunda linha também poderia ser escrita de forma infixa, mas é opcional:

(\\) xs ys = foldl (\zs y -> delete y zs) xs ys

Seções

Seções são um tipo de açúcar sintático que permite criamos novas funções a partir da aplicação parcial de operadores. Por exemplo, a função dobro que vimos antes pode ser definida como:

dobro x = (2*) x

Ou ainda, usando a notação de ponto livre:

dobro = (2*)

Assim sendo, podemos escrever a função dobrarLista de maneira ainda mais concisa, sem usar expressões lambda:

dobrarLista ls = map (*2) ls

Deve-se notar, entretanto, que para a maioria dos operadores, a ordem do argumento fixo importa:

Predefinição:HaskellGHCi

Se tentássemos definir (4 !!), por exemplo, teríamos um erro dizendo que o esperado é uma lista do lado esquerdo de (!!), e não um número.

Notação infixa com funções comuns

Se você tiver uma função comum, que deve ser usada na forma de prefixo, é possível usá-la na forma infixa, como um operador. Basta cercá-la de acentos graves `.

Um exemplo bastante comum é a função elem, que retorna True se um valor estiver contido numa lista, ou False, caso contrário:

Predefinição:HaskellGHCi

Este truque geralmente é usado para melhorar a leitura do código, e só pode ser usado com funções de dois argumentos.

Predefinição:Exercício