Gotas de Elixir – Manipulando listas

Manipular conjuntos de dados em Elixir é bem fácil utilizando os conceitos de Head e Tail junto com tail recursion e pattern matching!

Head e Tail

Ao receber uma lista como argumento de uma função, podemos dividi-la em duas partes: o primeiro elemento (Head) e o restante da lista (Tail). Veja no exemplo a seguir como é a sintaxe para receber um lista e dividi-la em head e tail:

defmodule PutsLista do
  def puts([ lista_head | lista_tail ]) do
    IO.puts("Head: #{lista_head}")
    IO.puts("Tail: #{lista_tail}")
  end
end
PutsLista.puts(["a", "b", "c"])
# mostra Head: a, Tail: bc

Dentro do corpo do método tanto a lista_head quanto lista_tail são utilizadas como variáveis. Lembre-se que lista_tail continua sendo uma lista, por tanto também possui um head e um tail.

Vamos modificar o código anterior para mostrar todos os elementos da lista um a um. Para isso vamos precisar criar duas funções diferentes, uma para processar uma lista normal e outra para um lista sem tail:

defmodule PutsLista do
  def puts([ lista_head | [] ]) do
    IO.puts("Head: #{lista_head}")
  end
  def puts([ lista_head | lista_tail ]) do
    IO.puts("Head: #{lista_head}")
    puts(lista_tail)
  end
end
PutsLista.puts(["a", "b", "c"])
# mostra Head: a, Head: b, Head: c

Utilizando o pattern matching, garantimos que a primeira definição de puts só será executada quando o tail da lista for uma lista vazia, ou seja quando a lista tiver apenas um elemento. Além disso, como a chamada recursiva ao puts é a última execução, garantimos também a otimização tail recursion.

O módulo Enum

Além da manipulação de listas com head e tail, Elixir também possui o módulo Enum que possui vários métodos úteis para lidar com listas. Assim você nem sempre precisará lidar diretamente com o head e tail.

O código anterior poderia ser reescrito utilizando Enum.each, que permite executar uma função (utilizamos a sintaxe para função anônima explicada nesse outro post) para cada um dos elementos da lista:

Enum.each(["a", "b", "c"], &(IO.puts("Head: #{&1}")))
# mostra Head: a, Head: b, Head: c

Outra função muito utilizada é a Enum.map, pois permite executar uma função em cada um dos elementos da lista mas ela coleta o retorno da função e cria um nova lista. Por exemplo:

Enum.map(["a", "b", "c"], &(String.upcase(&1)))
# Cria um nova lista ["A", "B", "C"]

Além de transformações também podemos filtrar elementos de uma lista com as funções Enum.filter (seleciona elementos de uma lista) e Enum.reject (rejeita elementos de uma lista). Ambas tomam a lista de elementos e uma função como argumento, mas o filter mantém elementos onde a função retorne true e o reject retira.

Enum.filter([1, 2, 3], &(Integer.is_even(&1)))
# Cria um nova lista [2]
Enum.reject([1, 2, 3], &(Integer.is_even(&1)))
# Cria um nova lista [1,3]

Além desses várias outras função do módulo Enum são úteis. Vale a pena conferir a documentação.

Confira mais conteúdo sobre Elixir!

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s