Perl | Chamadas de cauda na recursão de função
Recursão em Perl é qualquer função que não usa ofor
loop ou owhile
loop, em vez disso, chama a si mesma durante a execução do programa e a função correspondente é conhecida como função recursiva .
A função recursiva de cauda é um caso especial de recursão em que a instrução de chamada de função é executada como a ação final do procedimento. Então return loop(..);
funcionaria, mas return loop() + operation;
não funcionaria. A recursão da cauda (ou recursão da extremidade da cauda) é particularmente útil e, muitas vezes, fácil de manusear em implementações. A implementação da recursão final é feita principalmente para evitar o espaço ocupado pela estrutura de dados da pilha que mantém o controle dos dados retornados da instrução de chamada de recursão anterior.
A seguir estão alguns exemplos para uma melhor compreensão do conceito:
Exemplos 1:
#!/usr/bin/perl
# Perl Program to calculate Factorial
# Recursion without tail call
sub recurse_fact
{
my $x = $_[0];
# checking if that value is 0 or 1
if ($x == 0 || $x == 1)
{
return 1;
}
# Recursively calling function with
# the next value which is one less
# than current one
else
{
return $x * recurse_fact($x - 1);
}
}
# Recursion using Tail Call
sub tail_recurse_fact
{
my ($ans, $x) = @_;
# checking if that value is 0 or 1
if ($x == 0 || $x == 1)
{
return $ans;
}
# Recursively calling function with
# the next value which is one less
# than current one
else
{
return tail_recurse_fact($ans * $x, $x - 1);
}
}
# Driver Code
$a = 5;
# Function call and printing result after return
print "Factorial of a number $a ",
"through recursion is ", recurse_fact($a);
print "\nFactorial of a number $a ",
"through tail-recursion is ",
tail_recurse_fact(1, $a);
Fatorial de um número 5 até a recursão é 120 Fatorial de um número 5 até a recursão da cauda é 120
Na versão não final, o compilador precisa controlar o número pelo qual você vai multiplicá-lo, enquanto na versão final, o compilador pode perceber que o único trabalho restante a fazer é outra chamada de função e pode esqueça todas as variáveis e estados usados na função atual.
Exemplos 2:
#!/usr/bin/perl
# Perl program to demonstrate the
# use of tail-recursion
use strict;
use warnings;
sub recurse_fib
{
my $n = shift;
if ($n == 1 or $n == 2)
{
return 1
}
# recursive calling
return (recurse_fib($n - 1) +
recurse_fib($n - 2));
}
sub tail_recurse_fib
{
my ($n, $a, $b) = @_;
if ($n == 1)
{
return $a
}
if ($n == 2)
{
return $b
}
else
{
# tail recursive calling
return tail_recurse_fib($n - 1, $b, $a + $b)
}
}
# Driver code
$a = 10;
print "Fibonacci upto $a through recursion is ",
recurse_fib($a);
print "\nFibonacci upto $a through tail-recursion is ",
tail_recurse_fib($a, 1, 1);
Fibonacci até 10 através da recursão é 55 Fibonacci até 10 através da recursão da cauda é 55
Uso da goto
instrução para demonstrar Tail Recursion: goto
irá transferir o compilador para a sub-rotina do nome fornecido da sub-rotina em execução no momento. Isso substituirá a chamada de função e criará um processo de recursão da mesma maneira.
# Perl program to demonstrate the
# use of tail-recursion
use warnings;
sub recurse
{
my $i = shift;
return if $i == 0;
recurse($i - 1);
}
sub tail_recurse
{
my $i = shift;
return if $i == 0;
@_ = ($i - 1);
goto &tail_recurse;
}
# Driver Code
print "recursing\n";
recurse(200);
print "tail_recursing\n";
tail_recurse(200);
Saída:
recorrente tail_recursing
No exemplo acima, a recurse
função produzirá um erro fatal de 'recursão profunda' enquanto tail_recurse
funcionará bem.
Eliminação da chamada final
O recursivo final é melhor do que o recursivo não final, pois o recursivo final pode ser otimizado por compiladores modernos. O que um compilador moderno faz para otimizar o código recursivo final é conhecido como eliminação de chamada final . A eliminação da chamada final economiza espaço na pilha. Ele substitui uma chamada de função por uma instrução goto . Isso realmente não é eliminação - é uma otimização .
Às vezes, um método profundamente recursivo é a solução mais fácil para um problema, mas se você repetir mais de algumas centenas de chamadas, encontrará o erro de “recursão profunda” do Perl. Essa é a razão pela qual o recursivo de cauda é usado em vez de códigos recursivos de cauda não em Perl.
Se examinarmos mais de perto a função discutida acima, podemos remover a última chamada com goto. Abaixo estão exemplos de eliminação de chamada de cauda.
Exemplos 1: fatorial de um número
#!/usr/bin/perl
# Perl Program to calculate Factorial
sub tail_recurse_fact
{
my $ans = shift;
my $x = shift;
# checking if that value is 0 or 1
if ($x == 0 || $x == 1)
{
return $ans;
}
# Recursively calling function with
# the next value which is one less
# than current one
else
{
@_ = ($x * $ans, $x - 1);
goto &tail_recurse_fact;
}
}
# Driver Code
$a = 5;
# Function call and printing result after return
print "Factorial of a number $a ",
"through tail-recursion is ",
tail_recurse_fact(1, $a);
Fatorial de um número 5 até a recursão da cauda é 120
Exemplos 2: Fibonacci até n
#!/usr/bin/perl
# Perl program to demonstrate the
# use of tail-recursion-elimination
use strict;
use warnings;
sub tail_recurse_fib
{
my $n = shift;
my $a = shift;
my $b = shift;
if ($n == 1)
{
return $a
}
if ($n == 2)
{
return $b
}
else
{
@_ = ($n - 1, $b, $a + $b);
# tail recursive calling
goto &tail_recurse_fib;
}
}
# Driver code
$a = 10;
print "Fibonacci upto $a through tail-recursion is ",
tail_recurse_fib($a, 1, 1);
Fibonacci até 10 através da recursão é 55 Fibonacci até 10 através da recursão da cauda é 55
As postagens do blog Acervo Lima te ajudaram? Nos ajude a manter o blog no ar!
Faça uma doação para manter o blog funcionando.
70% das doações são no valor de R$ 5,00...
Diógenes Lima da Silva