Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
February 24, 2022 05:53 pm GMT

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

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To