您的递归函数调用不是无限的。只是需要很长时间。
人们应该注意递归函数,因为如果可以这样设计,最好使用循环来编写代码。
在这种情况下,您对fibonacci 的递归调用次数呈指数增长。您可以通过简单地添加较低级别的调用来非常轻松地自己测试这一点
-
fibonacci(0) = 1 次通话
-
fibonacci(1) = 1 次通话
-
fibonacci(2) = 1 次呼叫 + 呼叫 fibonacci(1) + 呼叫 fibonacci(0) → 3
-
fibonacci(3) = 1 次呼叫 + 呼叫 fibonacci(2) + 呼叫 fibonacci(1) → 5
-
fibonacci(4) = 1 次呼叫 + 呼叫 fibonacci(3) + 呼叫 fibonacci(2) → 9
-
fibonacci(5) = 1 次呼叫 + 呼叫 fibonacci(4) + 呼叫 fibonacci(3) → 15
-
fibonacci(N) = 1 次呼叫 + 呼叫 fibonacci(N-1) + 呼叫 fibonacci(N-2) → ?
如您所见,子例程调用的数量呈斐波那契式增长。
要确定fibonacci(100) 的调用次数,我们可以简单地创建一个快速的 perl 单行:
$ perl -e '
@c = (1, 1);
$c[$_] = 1 + $c[$_-1] + $c[$_-2] for (2..100);
print $c[100]
'
1.14629568802763e+21
以每秒 1 万亿次呼叫的速度,这仍需要 31 年多的时间才能完成。
使用 Memoize 修复
已经提出的一个修复方法是使用核心库Memoize
use Memoize;
memoize('fibonacci');
此模块将环绕您的子例程调用并缓存值的计算。因此,这会将您的函数调用次数从 1.14e21 减少到字面上的 101 次计算。
但是,由于您的递归次数超过 100 次,因此您将根据 perldiag 收到以下警告
改为创建循环 - 避免递归
您的算法可以很容易地从自上而下的计算方法重新设计为自下而上的方法。
这完全消除了递归的需要,并将代码简化为以下内容:
use strict;
use warnings;
print fibonacci(100), "\n";
sub fibonacci {
my $number = shift;
my @fib = ( 0, 1 );
for ( $#fib + 1 .. $number ) {
$fib[$_] = $fib[ $_ - 1 ] + $fib[ $_ - 2 ];
}
return $fib[$number];
}
输出:
3.54224848179262e+20
不会给出警告。