一個很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

相關文章
相關標籤/搜索