Entendendo Scala Sequence Comprehensions
Neste post explicarei a “mágica” por trás da sintaxe de sequence comprehensions em Scala..
1 2 3 4 5 6 | |
A sintaxe lembra um SELECT...JOIN em SQL onde a cláusula WHERE requer que
x e y sejam pares. O resultado da avaliação da expressão for acima é a
lista de todos os pares de números pares entre 1 e 6.
1
| |
A mesma lista poderia ser definida de outra forma.
1 2 3 4 5 | |
Sequence comprehensions são traduzidas para expressões equivalentes em termos
de flatMap, map, e withFilter que são métodos definidos em todas as
Collections da linguagem Scala. Como exemplo, nesse post, estou usando apenas
a
List, mas qualquer classe que defina flatMap, map e withFilter pode
ser usada dentro de expressões for como essas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | |
Para entendimento desse post, pode-se entender withFilter como sendo
indêntico ao método filter. A diferença é que em vez de alocar uma nova
lista, withFilter passa a lista e o predicado para a criação de uma instância
de WithFilter. WithFilter define map, flatMap, filter,
withFilter… mas restringe a aplicação dessas funções apenas aos valores que
satisfazem ao predicado da chamada a withFilter, comportando-se exatamente
como uma List resultante da aplicação de filter.
1 2 3 4 5 6 7 8 9 | |
Nesse exemplo, flatMap transforma uma lista de linhas de um texto em uma
lista de palavras de um texto. Para isso, basta prover a flatMap uma função
que transforma uma linha em uma lista de palavras. flatMap aplicará essa
função a cada linha e concatenará as listas resultantes para produzir o
resultado final.
Após entender as definições de withFilter, flatMap e map, vamos às regras
de tradução de sequence comprehensions.
1 for simples
1 2 3 4 5 | |
Em sua forma mais simples, um for é simplesmente um map.
1 2 3 4 5 6 | |
2 for com filtro
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
ifs são traduzidos para chamadas a withFilter.
3 for com sequência de geradores
1 2 3 4 5 6 | |
Quando há mais de um gerador, flatMap é usado. Você pode entender a sequência
de geradores como várias chamadas aninhadas a flatMap (regra 3) e filtros
quando necessário, que produzem uma única sequência que servirá para a
aplicação de map ao final. A função passada para map é construída a partir
da expressão após a palavra-chave yield.
Juntando tudo
Vamos traduzir as expressões for apresentadas no início do post.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
A primeira forma tem apenas um filtro (equivalente ao if x % 2 == 0 && y % 2 == 0).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
A segunda forma tem dois filtros (um para testar a paridade de x e outro para
testar a paridade de y).