偶然在国外一个网站瞅到的,非常的酷,发出来共享一下。一般来说,Python和Java,C#一样是没有尾递归自动优化的能力的,递归调用受到调用栈长度的限制被广泛的诟病,但是这个狂人用一个匪夷所思的方法解决了这个问题并在Python上实现了,从此Python的递归调用再也不用受到调用栈长度的制约,太酷了。
首先我们还是从递归说起,之前我发过一篇 《浅谈递归过程以及递归的优化》其中用到了斐波那契数来作为例子.线性递归的算法由于太过一低效就被我们Pass掉了,我们先来看尾递过方式的调用:
这段程序我们来测试一下,调用 Fib(1001)结果:
>>> def Fib(n,b1=1,b2=1,c=3):
... if n<3:
... return 1
... else:
... if n==c:
... return b1+b2
... else:
... return Fib(n,b1=b2,b2=b1+b2,c=c+1)
...
>>> Fib(1001)
70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501L
>>>
如果我们用Fib(1002),结果,茶几了,如下:
.....
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
RuntimeError: maximum recursion depth exceeded
>>>
好了,现在我们来尾递归优化
我们给刚才的Fib函数增加一个Decorator,如下:
1 @tail_call_optimized
恩,就是这个@tail_call_optimized的装饰器 ,这个装饰器使Python神奇的打破了调用栈的限制。
这下即使我们Fib(20000),也能在780ms跑出结果(780ms是以前博文提到那台2000元的上网本跑出来的结果)
不卖关子了,下面我们来看看这段神奇的代码:
使用的方法前面已经展示了,令我感到大开眼界的是,作者用了抛出异常然后自己捕获的方式来打破调用栈的增长,简直是太匪夷所思了。而且效率问题,和直接尾递归Fib相比大概造成了五倍的时间开销。
最后很不可思议的,尾递归优化的目的达成了。
本代码的出处:http://code.activestate.com/recipes/474088/
还有一个JavaScript的实现:http://w3future.com/weblog/2006/02/#tailCallEliminationInJavascript
如果有同学出了C#或者Java的实现,请发出来供大家瞻仰,呵呵