Compreensão (for)
Vamos começar um pouco diferente nesse capítulo. Vamos direto para o exemplo e ver a necessidade aparecer com o tempo e assim, explicar melhor o conceito de compreensão.
Exemplo
Precisamos filtrar uma lista de números e obter apenas os números pares. Vamos ao teste. Ele deve seguir as regras
Enviamos uma lista de inteiros para a função
Essa função nos retorna uma lista de apenas números pares
Também queremos apenas os numeros entre 0 e 10
Vamos crianos nosso teste primeiro:
Na linha 9 adicionamos uma confirmação de que teremos de resultado da operação for
uma lista. Se assemelhando ao Enum.map. Vamos rodar esse teste:
Como esperado, o relatório de erro nos trouxe que a função não existe. Vamos criar ela. Sua definição é Numbers.
just_pair_please/1
. Vamos replicar isso em nosso código
Criado a função, retornamos os valores que precisamos para fazer o teste passar:
Certo, teste passando. Vamos adicionar uma nova lista, para garantir que tudo funciona. Vamos ao teste:
Fizemos uma nova chamada a função passando uma lista diferente que vai de 1 a 5. O restultado esperado é 2 e 4, os únicos pares da lista. Vamos rodar isso para ver se tudo funciona com deveria.
Temos uma resposta diferente do esperado, isso porque estamos com valores estáticos na resposta da função. Precisamos agora deixar mais dinâmica as coisas. Precisamos percorrer a lista e retornar somente valores pares. Podemos fazer isso utilizando um Enum.filter/2
. Vamos tentar:
Utilizamos Enum.filter/2
para conseguir fazer essa operação. Seguinda ideia de que um número par deve ter como resto de divisão por 2 o valor 0. A função rem/2
pega o resto da operação (casas decimais) e converte para integer
. Uma vez que for 0 o valor é par. Em Enum.filter/2
uma vez que seja true o valor, o elemento ficará na lista, quando for falso, será removido. Vamos rodar o teste:
Perfeito! As coisas estão melhorando.
Enviamos uma lista de inteiros para a funçãoEssa função nos retorna uma lista de apenas números paresTambém queremos apenas os numeros entre 0 e 10
Dois pontos foram resolvidos. Precisamos agora permitir que o range de nosso filtro seja apenas para números até o número 10. Vamos criar uma teste para isso:
Um teste praticamente igual ao anterior, porém, com o range maior, indo de 1 a 10. Nossa nova regra é, os números devem apenas ir até 10. Vamos rodar o teste":
Queriamos os números até 10. Mas temos a resposta dos pares com valores até 40. Então um relatório de erro foi mostrado. Vamos arrumar isso.
Adicionamos uma nova validação em nosso filtro. Uma condição if em que o número que for maior que 10, deve voltar false
e com isso, não entrar na lista.
Vamos rodar o teste:
Funcionou perfeitamente. Agora vamos adicionar um novo comportamento. Os dados que restaram em nosso filtro devem ser multiplicados por 3.
Enviamos uma lista de inteiros para a funçãoEssa função nos retorna uma lista de apenas números paresTambém queremos apenas os numeros entre 0 e 10Valores restantes na lista devem ser multiplicados por 3
Vamos alterar nossos testes:
Vamos rodar o teste:
Duas falhas. Nosso teste esta pedindo que os valores retornados sejam multiplicado por e. com isso precisamos realizar uma transformação em nossa lista. Mas Enum.filter/2
não serve para isso, ele serve apenas para filtrar. Quem faz transformações de um enumerável é o Enum.map
/2
. Vamos ter que obter o resultado do Enum.filter/2
, para conseguir usar em um Enum.map/2
. Vamos implementar:
Colocamos a lista filtrada em uma variável chamada list_filtered
. Depois utilizamos o Enum.map/2
para realizar a transformação, passando list_filtered
como primeiro argumento e o segundo a função de transformação, obtendo o valor de um número e multiplicando por 3.
Vamos executar o teste para ver como estamos:
Conseguimos realizar todas as regras de negócio que precisamos.
Enviamos uma lista de inteiros para a funçãoEssa função nos retorna uma lista de apenas números paresTambém queremos apenas os numeros entre 0 e 10Valores restantes na lista devem ser multiplicados por 3
Mas em um problema relativamente simples, temos um código maior. Não é necessariamente um problema, o código é claro e fácil de entender após lermos com atenção. Porém, existe uma forma de resumir isso caso você queira. Chamamos isso de sintaxy sugar.
Em elixir temos a syntax sugar chamada Comprehension
que é criado em três partes: filtro, transformação e coleção. Justamente o que precisamos agora. Uma explicação rápida:
Compreensão
Compreensões em Elixir são uma forma concisa e expressiva de criar, filtrar, transformar e combinar coleções em uma única linha de código. As compreensões em Elixir são inspiradas nas compreensões de listas em Python e nas expressões de conjunto em matemática.
Uma compreensão é composta por três partes principais:
A cláusula
for
: que especifica uma ou mais variáveis e as expressões que geram os valores para essas variáveis.
A cláusula
do
: que define as transformações a serem aplicadas a cada valor gerado pela cláusulafor
.
Opcionalmente, cláusulas adicionais, como
if
ouunless
, que filtram os valores gerados pela cláusulafor
.
Vamos voltar ao exemplo
Refatorando
Não precisamos mais tocar no teste, uma vez que toda a regra de negócio está refletida lá. O que precisamos agora é deixar nosso implementação melhor, vamos lá:
O que vocês acham da nova implementação? Bem mais clara, não acham? Vamos rodar os testes:
Perfeito, refatoramos de forma a ficar mais claro e fácil de entender. Mantivemos todos os testes passando o que garante o funcionamento de nossa implementação.
Conclusão
Compreensão pode ser útil para diminuir o tamanho de nosso código. Porém isso pode causar problemas de legibilidade, então, minha dica é: entendam o que vocês precisam no momento e tomem uma decisão conciente. Tanto um quanto o outro estão corretos.
Last updated