Haskell · sintaxe · listas
As list comprehensions em Haskell, explicadas
Uma das primeiras coisas que tornam Haskell elegante é a list comprehension: uma forma concisa de construir uma nova lista a partir de listas existentes, espelhando a notação matemática por compreensão de que talvez se lembre da escola. Este guia explica a sintaxe, os geradores múltiplos, as guardas e a relação entre as comprehensions e map e filter.
A sintaxe básica
Uma list comprehension tem a forma [ output | generator, condition ]. Leia a barra como «tal que»:
-- the squares of 1 to 10
squares = [x*x | x <- [1..10]]
-- [1,4,9,16,25,36,49,64,81,100] Aqui x <- [1..10] é um gerador: extrai cada x da lista [1..10], e a parte antes da barra, x*x, é a expressão de saída aplicada a cada elemento.
Adicionar guardas (filtragem)
Uma guarda é uma condição booleana que conserva apenas os elementos que a satisfazem:
-- even numbers from 1 to 20, doubled
result = [x*2 | x <- [1..20], even x]
-- [4,8,12,16,20,24,28,32,36,40] Pode combinar várias guardas, separadas por vírgulas; todas têm de ser verdadeiras para que um elemento seja incluído.
Geradores múltiplos
Com mais do que um gerador, a comprehension itera sobre cada combinação (como ciclos aninhados), por ordem:
-- all pairs from two small lists
pairs = [(x, y) | x <- [1,2,3], y <- ['a','b']]
-- [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b')] O gerador mais à direita varia mais depressa. Também pode fazer um gerador posterior depender de um anterior — por exemplo, extraindo y de [x..10].
A sua relação com map e filter
Uma comprehension é essencialmente map e filter numa única forma legível. Estes são equivalentes:
[x*2 | x <- xs, even x]
map (*2) (filter even xs) Use o que se ler melhor em cada caso. As comprehensions brilham quando tem vários geradores ou várias guardas; map/filter compõem-se bem em pipelines point-free. Como Haskell é preguiçoso, uma comprehension sobre uma lista infinita como [1..] só calcula os elementos que de facto consome.
FAQ
O que é um gerador numa list comprehension? A parte x <- xs: extrai cada valor de x da lista xs para alimentar a expressão de saída.
O que é uma guarda? Uma condição booleana (como even x) colocada depois de uma vírgula; só se conservam os elementos para os quais é verdadeira.
Posso usar vários geradores? Sim — a comprehension produz cada combinação, com o gerador mais à direita a variar mais depressa, como ciclos aninhados.
As comprehensions são apenas map e filter? Conceptualmente sim para os casos simples; as comprehensions são muitas vezes mais legíveis com vários geradores ou guardas, ao passo que map/filter se compõem bem em pipelines.
Para a linguagem por trás da sintaxe, veja o que é Haskell; as comprehensions são uma das funcionalidades que o tornam conciso e expressivo.