An Interest In:
Web News this Week
- April 18, 2024
- April 17, 2024
- April 16, 2024
- April 15, 2024
- April 14, 2024
- April 13, 2024
- April 12, 2024
Mas e os loops em Elixir?
Se voc est vindo de uma linguagem de paradigma imperativo, orientada a objetos, muito provavelmente voc vai esbarrar nessa pergunta do ttulo.
Pois bem, Elixir no possui esse termo em seu vocabulrio. Apesar de ser possvel iterar sobre uma lista de elementos utilizando o for
:
iex> for n <- [1, 2, 3, 4], do: n * n[1, 4, 9, 16]
Isso no significa que o Elixir possui loops, esse for
nada mais do que uma chamada nativa ao List Comprehensions do Erlang.
Curioso n? Como fazer para lidar com uma coleo de itens ento?
H 2 maneiras de trabalhar com este tipo de problema, sendo elas:
- High Order Functions (map, reduce, filter, find, etc...)
- Recursividade
High Order Functions
muito comum se deparar com uma situao onde voc tem uma lista de elementos e precisa manipular os dados dela.
E para isso podemos utilizar funes como map, reduce, filter, etc.
Digamos que voc precise multiplicar todos os elementos da sua lista por 2, isso deveria ser um problema trivial, certo?
A implementao desse problema em js utilizando uma estrutura de repetio (loop) seria mais ou menos assim:
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];for (let i = 0; i < list.length; i++) { list[i] = list[i] * 2;}> list> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Em Elixir possvel resolver isso com map/2
:
iex> list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]iex> sum = fn x -> x * 2 endiex> Enum.map(list, sum)[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Comparando os exemplos acima, diferente da estrutura de repetio, com o map/2
no necessrio definir nada alm da frmula para mapear os dados da minha lista, onde a frmula x -> x * 2
.
O prprio map/2
faz o resto do trabalho aplicando a frmula pra cada elemento da nossa lista, e gerando uma nova lista ao final da execuo.
Segue o mesmo conceito para as demais funes, como:
map/2
manipula os elementos e gera uma nova listafilter/2
filtra os elementos e gera uma nova listareduce/2
manipula os elementos acumulando seus resultados anteriores
Existem diversos tipos de High Order Functions disponveis no Elixir, feitas para facilitar a sua vida na hora de resolver problemas sejam eles triviais ou no.
Recursividade
Mesmo que voc seja uma pessoa de linguagens imperativas, o termo recursividade ainda algo familiar!
E o que recursividade? uma forma de iterar sob listas (ou no), onde a funo chama ela prpria at atingir uma condio de parada.
Um exemplo muito conhecido sobre recursividade, o clculo fatorial de um nmero, onde 4! = 4 x 3 x 2 x 1 = 24
Destrinchando esse clculo, teramos algo semelhante a:
4! = 3! * 4 = 2! * 3 = 1! * 2 = 1
Considerando o exemplo acima, a condio de parada para essa execuo o valor 1. Quando a funo fatorial receber como argumento o valor 1, ela dever retornar 1 e as outras execues devem se basear neste valor, exemplo:
4! = (6) * 4 = 24 = (2) * 3 = (1) * 2 = 1
Em parnteses: resultado da chamada fatorial anterior.
A implementao em JS:
fact = (val) => { if (val == 1) return 1; return fact(val - 1) * val;};
Essa mesma funo em Elixir fica assim:
def fact(1), do: 1def fact(a), do: fact(a - 1) * a
Nota-se que na chamada recursiva, a funo fact/1
chama a si mesma passando o argumento - 1
, e quando essa funo chamada com o valor 1, ela retorna somente o valor 1 e encerra sua execuo em cadeia.
Vamos analisar mais de perto essa chamada do fact/1
:
# Considere que estamos chamando fatorial com o argumento 4 (4!)fact(4) -> fact(4 - 1) * 4 # Esse ser o retorno# Ele estar chamando fact(3) e multiplicando por 4.# E assim sucessivamente...fact(3) -> fact(3 - 1) * 3fact(2) -> fact(2 - 1) * 2fact(1) -> 1# Essa abordagem gera uma cadeia de chamadas que precisam ser resolvidas.# Quando a chamada em cadeia chega na nossa condio de parada (1)# O processador comea a desencadear essas chamadas que empilhamos.# Seguindo assim:fact(1) -> 1fact(2) -> (1) * 2 -> 2fact(3) -> (2) * 3 -> 6fact(4) -> (6) * 4 -> 24
Implementar uma funo fatorial utilizando recursividade bem simples, n? Mas se formos considerar a explicao que acabamos de ver, isso pode se tornar um problema?
Imagine que temos uma funo que precisar iterar milhares de vezes para resolver um determinado problema, precisaremos empilhar vrias chamadas no-resolvidas na nossa pilha de chamadas, e isso poder estourar o limite da pilha.
Para esse problema em especfico, h uma soluo chamada Tail Call Optimization (ou TCO). Com TCO possvel eliminar essas chamadas no-resolvidas que uma funo recursiva costuma criar.
O pulo do gato quando aplicamos Tail Call Optimization em uma funo recursiva que essa funo saiba o valor processado em todas as suas chamadas, sendo assim, ela no depende do desencadeamento para encontrar o valor final de sua execuo.
E como podemos fazer isso? Segue o exemplo de uma chamada sem TCO:
# nossa condio de paradadef fact(1), do: 1# funo fatorial recursivadef fact(val), do: fact(val - 1) * val
com TCO:
def fact(val), do: fact(val - 1, val)# nossa condio de paradadefp fact(1, res), do: res# funo fatorial recursivadefp fact(val, res), do: fact(val - 1, res * val)
A grande diferena, que na funo fatorial com TCO, ela sabe exatamente o valor da sua execuo.
fact(4) -> fact(4 - 1, 4)fact(3, 4) -> fact(3 - 1, (4 * 3)) -> fact(2, 12)fact(2, 12) -> fact(2 - 1, (12 * 2)) -> fact(1, 24)fact(1, 24) -> 24
Portanto, quando a nossa funo chega na sua condio de parada, no necessrio desencadear todas as chamadas anteriores e seus respectivos clculos. Ela s precisa retornar seu valor final (24) para a funo que originou a sua chamada fact(4)
.
Fato curioso: Por padro, Elixir/Erlang implementam Tail Call Optimization, por isso a utilizao de recursividade algo muito comum e encorajada! Inclusive as High Order Functions so implementadas atravs de recursividade, no final das contas.
Concluso
Elixir amor, Erlang vida.
Original Link: https://dev.to/wlsf/mas-e-os-loops-em-elixir-3d0k
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To