一个很Cool的idea->Python的尾递归优化(转)

偶然在国外一个网站瞅到的,很是的酷,发出来共享一下。通常来讲,Python和Java,C#同样是没有尾递归自动优化的能力的,递归调用受到调 用栈长度的限制被普遍的诟病,可是这个狂人用一个匪夷所思的方法解决了这个问题并在Python上实现了,今后Python的递归调用不再用受到调用栈 长度的制约,太酷了。html

首先咱们仍是从递归提及,以前我发过一篇 《浅谈递归过程以及递归的优化》其中用到了斐波那契数来做为例子.线性递归的算法因为太过一低效就被咱们Pass掉了,咱们先来看尾递过方式的调用:python

复制代码

1 def Fib(n,b1=1,b2=1,c=3):
2     if n<3:
3         return 1
4     else:
5         if n==c:
6             return b1+b2
7         else:
8             return Fib(n,b1=b2,b2=b1+b2,c=c+1)
9 web

复制代码

 

 

这段程序咱们来测试一下,调用 Fib(1001)结果:算法

>>> def Fib(n,b1=1,b2=1,c=3):函数

...     if n<3:测试

...         return 1优化

...     else:网站

...         if n==c:this

...             return b1+b2spa

...         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,以下:

@tail_call_optimized
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)


 

 

恩,就是这个@tail_call_optimized的装饰器 ,这个装饰器使Python神奇的打破了调用栈的限制。

这下即便咱们Fib(20000),也能在780ms跑出结果(780ms是之前博文提到那台2000元的上网本跑出来的结果)

 

不卖关子了,下面咱们来看看这段神奇的代码:

复制代码

 1 import sys  
 2   
 3 class TailRecurseException:  
 4   def __init__(self, args, kwargs):  
 5     self.args = args  
 6     self.kwargs = kwargs  
 7   
 8 def tail_call_optimized(g):  
 9   """  
10   This function decorates a function with tail call  
11   optimization. It does this by throwing an exception  
12   if it is it's own grandparent, and catching such  
13   exceptions to fake the tail call optimization.  
14     
15   This function fails if the decorated  
16   function recurses in a non-tail context.  
17   """  
18   def func(*args, **kwargs):  
19     f = sys._getframe()  
20     if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code:  
21       raise TailRecurseException(args, kwargs)  
22     else:  
23       while 1:  
24         try:  
25           return g(*args, **kwargs)  
26         except TailRecurseException, e:  
27           args = e.args  
28           kwargs = e.kwargs  
29   func.__doc__ = g.__doc__  
30   return func  
31 

复制代码

 

 

使用的方法前面已经展现了,令我感到大开眼界的是,做者用了抛出异常而后本身捕获的方式来打破调用栈的增加,简直是太匪夷所思了。并且效率问题,和直接尾递归Fib相比大概形成了五倍的时间开销。

最后很难以想象的,尾递归优化的目的达成了。

 

本代码的出处:http://code.activestate.com/recipes/474088/

 

 还有一个JavaScript的实现:http://w3future.com/weblog/2006/02/#tailCallEliminationInJavascript

相关文章
相关标签/搜索