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.