Comprehensions
Comprehensions são um ‘syntactic sugar’ (uma forma mais simples de escrever) para realizar loops em Enumerables em Elixir. Nesta lição veremos como podemos fazer iterações e gerar os resultados utilizando comprehensions.
Básico
Em alguns casos comprehensions podem ser usadas para produzir código mais conciso para fazer iterações com Enum
e Stream
. Vamos começar olhando para uma comprehension simples e então observar suas várias partes:
iex> list = [1, 2, 3, 4, 5]
iex> for x <- list, do: x*x
[1, 4, 9, 16, 25]
A primeira coisa que observamos é o uso de for
e um generator (gerador). O que é um generator? Generators são as expressões x <- [1, 2, 3, 4]
encontradas em comprehensions. Eles são responsáveis por gerar o próximo valor.
Para nossa sorte, comprehensions não são limitadas a listas; na verdade elas funcionam com qualquer enumerable:
# Keyword Lists
iex> for {_key, val} <- [one: 1, two: 2, three: 3], do: val
[1, 2, 3]
# Maps
iex> for {k, v} <- %{"a" => "A", "b" => "B"}, do: {k, v}
[{"a", "A"}, {"b", "B"}]
# Binaries
iex> for <<c <- "hello">>, do: <<c>>
["h", "e", "l", "l", "o"]
Como muitas outras coisas em Elixir, generators se apoiam no pattern matching para comparar a entrada definida na variável à esquerda. Caso um match não seja encontrado, o valor é ignorado.
iex> for {:ok, val} <- [ok: "Hello", error: "Unknown", ok: "World"], do: val
["Hello", "World"]
É possível utilizar múltiplos generators, bem como loops aninhados:
iex> list = [1, 2, 3, 4]
iex> for n <- list, times <- 1..n do
...> String.duplicate("*", times)
...> end
["*", "*", "**", "*", "**", "***", "*", "**", "***", "****"]
Para ilustrar melhor cada iteração do loop, vamos usar IO.puts
para mostrar os dois valores gerados:
iex> for n <- list, times <- 1..n, do: IO.puts "#{n} - #{times}"
1 - 1
2 - 1
2 - 2
3 - 1
3 - 2
3 - 3
4 - 1
4 - 2
4 - 3
4 - 4
Comprehensions são syntactic sugar e devem ser utilizadas apenas quando for apropriado.
Filtros
Você pode pensar em filtros como um tipo de guard para comprehensions. Quando um valor filtrado retorna false
ou nil
ele é excluído da lista final. Vamos iterar por um intervalo e olhar apenas os números pares. Nós vamos usar a função is_even/1
do módulo Integer para checar se um valor é par ou não.
import Integer
iex> for x <- 1..10, is_even(x), do: x
[2, 4, 6, 8, 10]
Assim como os generators, nós podemos usar múltiplos filtros. Vamos expandir nosso intervalo e então filtrar apenas para valores que sejam pares e também divisíveis por 3.
import Integer
iex> for x <- 1..100,
...> is_even(x),
...> rem(x, 3) == 0, do: x
[6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
Usando :into
E se nós quisermos produzir algo que não seja uma lista? Passando a opção :into
nós podemos fazer exatamente isso! Como uma regra geral, :into
aceita qualquer estrutura que implemente o protocolo Collectable
.
Usando :into
, vamos criar um mapa de uma lista de palavras-chave.
iex> for {k, v} <- [one: 1, two: 2, three: 3], into: %{}, do: {k, v}
%{one: 1, three: 3, two: 2}
Como bitstrings implementam collectables nós podemos usar comprehensions e :into
para criar strings:
iex> for c <- [72, 101, 108, 108, 111], into: "", do: <<c>>
"Hello"
É isso! Comprehensions são um modo fácil de iterar por coleções de maneira concisa.
Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!