Erlang - Recursão
A recursão é uma parte importante do Erlang. Primeiro, vamos ver como podemos implementar recursão simples implementando o programa fatorial.
Exemplo
-module(helloworld).
-export([fac/1,start/0]).
fac(N) when N == 0 -> 1;
fac(N) when N > 0 -> N*fac(N-1).
start() ->
X = fac(4),
io:fwrite("~w",[X]).
As seguintes coisas precisam ser observadas sobre o programa acima -
Estamos primeiro definindo uma função chamada fac (N).
Podemos definir a função recursiva chamando fac (N) recursivamente.
O resultado do programa acima é -
Resultado
24
Abordagem prática para recursão
Nesta seção, entenderemos em detalhes os diferentes tipos de recursões e seu uso em Erlang.
Recursão de comprimento
Uma abordagem mais prática para a recursão pode ser vista com um exemplo simples que é usado para determinar o comprimento de uma lista. Uma lista pode ter vários valores, como [1,2,3,4]. Vamos usar a recursão para ver como podemos obter o comprimento de uma lista.
Example
-module(helloworld).
-export([len/1,start/0]).
len([]) -> 0;
len([_|T]) -> 1 + len(T).
start() ->
X = [1,2,3,4],
Y = len(X),
io:fwrite("~w",[Y]).
As seguintes coisas precisam ser observadas sobre o programa acima -
A primeira função len([]) é usado para a condição de caso especial se a lista estiver vazia.
o [H|T] padrão para combinar com listas de um ou mais elementos, como uma lista de comprimento um será definido como [X|[]] e uma lista de comprimento dois será definida como [X|[Y|[]]]. Observe que o segundo elemento é a própria lista. Isso significa que só precisamos contar o primeiro e a função pode chamar a si mesma no segundo elemento. Dado que cada valor em uma lista conta como um comprimento de 1.
O resultado do programa acima será -
Output
4
Recursão de cauda
Para entender como funciona a recursão de cauda, vamos entender como funciona o código a seguir na seção anterior.
Syntax
len([]) -> 0;
len([_|T]) -> 1 + len(T).
A resposta para 1 + len (Rest) precisa da resposta de len (Rest) para ser encontrada. A própria função len (Rest) então precisava do resultado de outra chamada de função para ser encontrada. As adições seriam empilhadas até que a última fosse encontrada, e somente então o resultado final seria calculado.
A recursão de cauda visa eliminar esse empilhamento de operações, reduzindo-os à medida que acontecem.
Para conseguir isso, precisaremos manter uma variável temporária extra como um parâmetro em nossa função. A variável temporária mencionada é algumas vezes chamada de acumulador e atua como um local para armazenar os resultados de nossos cálculos à medida que eles acontecem, a fim de limitar o crescimento de nossas chamadas.
Vejamos um exemplo de recursão de cauda -
Example
-module(helloworld).
-export([tail_len/1,tail_len/2,start/0]).
tail_len(L) -> tail_len(L,0).
tail_len([], Acc) -> Acc;
tail_len([_|T], Acc) -> tail_len(T,Acc+1).
start() ->
X = [1,2,3,4],
Y = tail_len(X),
io:fwrite("~w",[Y]).
O resultado do programa acima é -
Output
4
Duplicado
Vejamos um exemplo de recursão. Desta vez, vamos escrever uma função que tenha um inteiro como seu primeiro parâmetro e qualquer outro termo como seu segundo parâmetro. Em seguida, ele criará uma lista de tantas cópias do termo quanto especificado pelo número inteiro.
Vejamos como seria um exemplo disso -
-module(helloworld).
-export([duplicate/2,start/0]).
duplicate(0,_) ->
[];
duplicate(N,Term) when N > 0 ->
io:fwrite("~w,~n",[Term]),
[Term|duplicate(N-1,Term)].
start() ->
duplicate(5,1).
O resultado do programa acima será -
Resultado
1,
1,
1,
1,
1,
Lista de reversão
Não há limites para os quais você possa usar a recursão em Erlang. Vejamos rapidamente como podemos reverter os elementos de uma lista usando recursão. O programa a seguir pode ser usado para fazer isso.
Exemplo
-module(helloworld).
-export([tail_reverse/2,start/0]).
tail_reverse(L) -> tail_reverse(L,[]).
tail_reverse([],Acc) -> Acc;
tail_reverse([H|T],Acc) -> tail_reverse(T, [H|Acc]).
start() ->
X = [1,2,3,4],
Y = tail_reverse(X),
io:fwrite("~w",[Y]).
O resultado do programa acima será -
Resultado
[4,3,2,1]
As seguintes coisas precisam ser observadas sobre o programa acima -
Estamos novamente usando o conceito de variáveis temporárias para armazenar cada elemento da Lista em uma variável chamada Acc.
Nós então ligamos tail_reverse recursivamente, mas desta vez, garantimos que o último elemento seja colocado primeiro na nova lista.
Em seguida, chamamos recursivamente tail_reverse para cada elemento na lista.