Elixir - Enumeráveis
Um enumerável é um objeto que pode ser enumerado. "Enumerado" significa contar os membros de um conjunto / coleção / categoria, um por um (normalmente em ordem, geralmente por nome).
Elixir fornece o conceito de enumeráveis e o módulo Enum para trabalhar com eles. As funções no módulo Enum são limitadas, como o nome diz, a enumerar valores em estruturas de dados. Exemplo de uma estrutura de dados enumerável é uma lista, tupla, mapa, etc. O módulo Enum nos fornece um pouco mais de 100 funções para lidar com enums. Discutiremos algumas funções importantes neste capítulo.
Todas essas funções tomam um enumerável como o primeiro elemento e uma função como o segundo e trabalham sobre eles. As funções são descritas a seguir.
todos?
Quando usamos all? função, a coleção inteira deve ser avaliada como verdadeira, caso contrário, falso será retornado. Por exemplo, para verificar se todos os elementos da lista são números ímpares, então.
res = Enum.all?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end)
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
false
Isso ocorre porque nem todos os elementos desta lista são estranhos.
qualquer?
Como o nome sugere, esta função retorna verdadeiro se qualquer elemento da coleção for avaliado como verdadeiro. Por exemplo -
res = Enum.any?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end)
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
true
pedaço
Esta função divide nossa coleção em pequenos pedaços do tamanho fornecido como o segundo argumento. Por exemplo -
res = Enum.chunk([1, 2, 3, 4, 5, 6], 2)
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
[[1, 2], [3, 4], [5, 6]]
cada
Pode ser necessário iterar sobre uma coleção sem produzir um novo valor, para este caso, usamos o each função -
Enum.each(["Hello", "Every", "one"], fn(s) -> IO.puts(s) end)
Quando o programa acima é executado, ele produz o seguinte resultado -
Hello
Every
one
mapa
Para aplicar nossa função a cada item e produzir uma nova coleção, usamos a função de mapa. É uma das construções mais úteis na programação funcional, pois é bastante expressiva e curta. Vamos considerar um exemplo para entender isso. Vamos dobrar os valores armazenados em uma lista e armazená-los em uma nova listares -
res = Enum.map([2, 5, 3, 6], fn(a) -> a*2 end)
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
[4, 10, 6, 12]
reduzir
o reducefunção nos ajuda a reduzir nosso enumerável a um único valor. Para fazer isso, fornecemos um acumulador opcional (5 neste exemplo) para ser passado para nossa função; se nenhum acumulador for fornecido, o primeiro valor será usado -
res = Enum.reduce([1, 2, 3, 4], 5, fn(x, accum) -> x + accum end)
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
15
O acumulador é o valor inicial passado para o fn. Da segunda chamada em diante, o valor retornado da chamada anterior é passado como acum. Também podemos usar reduzir sem o acumulador -
res = Enum.reduce([1, 2, 3, 4], fn(x, accum) -> x + accum end)
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
10
uniq
A função uniq remove duplicatas de nossa coleção e retorna apenas o conjunto de elementos da coleção. Por exemplo -
res = Enum.uniq([1, 2, 2, 3, 3, 3, 4, 4, 4, 4])
IO.puts(res)
Ao executar o programa acima, ele produz o seguinte resultado -
[1, 2, 3, 4]
Avaliação Eager
Todas as funções no módulo Enum estão ansiosas. Muitas funções esperam um enumerável e retornam uma lista de volta. Isso significa que ao realizar várias operações com Enum, cada operação vai gerar uma lista intermediária até chegarmos ao resultado. Vamos considerar o seguinte exemplo para entender isso -
odd? = &(odd? = &(rem(&1, 2) != 0)
res = 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum
IO.puts(res)
Quando o programa acima é executado, ele produz o seguinte resultado -
7500000000
O exemplo acima possui um pipeline de operações. Começamos com um intervalo e, em seguida, multiplicamos cada elemento no intervalo por 3. Esta primeira operação criará e retornará uma lista com 100_000 itens. Em seguida, mantemos todos os elementos ímpares da lista, gerando uma nova lista, agora com 50_000 itens, e então somamos todas as entradas.
o |> símbolo usado no trecho acima é o pipe operator: ele simplesmente pega a saída da expressão em seu lado esquerdo e a passa como o primeiro argumento para a chamada de função em seu lado direito. É semelhante ao Unix | operador. Seu objetivo é destacar o fluxo de dados sendo transformado por uma série de funções.
Sem o pipe operador, o código parece complicado -
Enum.sum(Enum.filter(Enum.map(1..100_000, &(&1 * 3)), odd?))
Temos muitas outras funções, no entanto, apenas algumas importantes foram descritas aqui.