An Interest In:
Web News this Week
- April 25, 2024
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
- April 20, 2024
- April 19, 2024
Funes recursivas e annimas em Elixir
English version of this text here!
Um tempo atrs, eu tentei criar uma funo recursiva e annima em Elixir porque eu estava dentro do iex e estava com preguia de criar um mdulo dentro do repl. Minha primeira tentativa foi algo assim:
# Fatorial ingnuo, annimo e recursivofact = fn 0 -> 1 n -> n * fact.(n-1)end
OBS: Sim, possvel definir mltiplas clusulas para funes annimas!
claro que isso no funcionou, porque fact/1
ainda no est definida no momento em que chamada dentro do corpo da funo.
Eu realmente no lembro por que eu precisava de uma funo annima e recursiva, mas no comeo dessa semana eu percebi o que que eu tinha que fazer para que funcionasse. Tudo o que temos que fazer assegurar-nos que a funo existe no momento em que chamada, certo?
Ento e se a funo annima recebesse outra funo como argumento, e essa funo recebida fosse o que faz a chamada recursiva?
Agora o Elixir no poderia reclamar da funo no estar definida, porque ela foi fornecida via um argumento, que poderia ser qualquer coisa:
fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1)end
Bom, mas o que essa funo recur
?
Ela poderia muito bem ser a prpria fact
! Somente temos que pass-la quanto a estivermos chamando:
fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1)end# Passe fact para si mesma, para que ela seja "recur" dentro do corpo da funofact.(4, fact)
claro que isso vai lanar uma exceo, pois agora fact tem uma aridade de 2, e recur est sendo chamada com apenas um argumento. Lembre-se que recur de "facto" fact! Sim, tentei fazer um trocadilho.
Para resolver isso, tudo que temos que fazer chamar recur passando recur para si mesmo, assim:
fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1, recur)endfact.(4, fact)#=> 24
Tambm funciona com recurso de cauda, claro. Tudo que teramos que fazer gerenciar os parmetros e retornos corretamente.
Legal, posso usar isso num Enum.map ou alguma outra funo de ordem superior?
Bom, somente se tomarmos cuidado.
Se escrevermos isso:
fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1, recur)endEnum.map([1,2,3,4], fact)
Ento Enum.map/2 tentar invocar fact
com um argumento apenas, lanando uma exceo.
Uma maneira de resolver isso seria encapsulando-a dentro de uma outra funo:
fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1, recur)endEnum.map([1,2,3,4], &(fact.(&1, fact)))#=> [1, 2, 6, 24]
Outra maneira seria definir fact com uma interface mais legal.
A funo fact poderia simplesmente receber um argumento e da definir a funo annima e recursiva dentro de si mesma, e da chamar a funo annima recm definida passando a mesma como argumento.
Seria algo assim:
fact = fn n -> do_fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1, recur) end do_fact.(n, do_fact)endEnum.map([1,2,3,4], fact)#=> [1, 2, 6, 24]
Agora ns temos uma funo recursiva e annima que funciona como o esperado. Se eu quiser saber o fatorial de um nmero, apenas tenho que passar o nmero para a funo, que exatamente o que Enum.map tentar fazer.
At definir na mesma linha essa funo annima funcionar:
Enum.map([1,2,3,4], fn n -> do_fact = fn 0, _ -> 1 n, recur -> n * recur.(n-1, recur) end do_fact.(n, do_fact)end)#=> [1, 2, 6, 24]
E recursiva de cauda:
Enum.map([1,2,3,4], fn n -> do_fact = fn 0, acc, _ -> 1 n, acc, recur -> recur.(n-1, n*acc, recur) end # Lembre-se de definir corretamente o valor inicial do acumulador. Aqui ele 1 do_fact.(n, 1, do_fact)end)#=> [1, 2, 6, 24]
Ter uma interface mais legal no bom somente para us-la como argumentos de uma funo de order superior, tambm mais legal para ns invocarmos essa funo, claro:
fact = fn n -> do_fact = fn 0, acc, _ -> 1 n, acc, recur -> recur.(n-1, n*acc, recur) end do_fact.(n, 1, do_fact)end# No h necessidade de passar nem o acumulador nem a prpria funofact.(4)#=> 24
Agora se eu quiser saber o fatorial de 4, tenho que apenas chamar a funo fact/1
com 4 como argumento, sem ter essas coisas estranhas de passar a prpria funo por a manualmente na chamada.
Legal, mas isso til?
Para ser sincero, eu no sei.
Para comeo de conversa, no ilegal definir um mdulo dentro do repl, ento o cenrio de "Estou com preguia dentro do iex" no to importante assim. Quero dizer, eu tive que escrever tanta coisa a mais s para criar essa funo recursiva e annima que talvez simplesmente escrever defmodule M do ... end
seja mais fcil e mais rpido.
Outra situao em que poderamos usar isso seria em arquivos elixir normais onde temos algums Enums ou outras funes de ordem superior trabalhando, mas da a gente provavelmente j vai estar dentro de um mdulo, onde poderamos definir uma funo privada "com nome" (no annima) para fazer esse trabalho e j dando um nome significativo a ela, aproveitando que vamos estar ali. Isso vai culminar num cdigo melhor e mais legvel do que a "soluo" com as funes recursivas e annimas malucas.
Mas ei, mesmo que isso no seja assim to til, pelo menos sempre legal pensar sobre funes e recurso, certo?
E tambm foi bem divertido escrever esse post!
isso por hoje. Obrigado por ler e tenha um bom dia! (:
Original Link: https://dev.to/lucassperez/funcoes-recursivas-e-anonimas-em-elixir-347i
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To