一個很Cool的Idear->Python的尾遞迴最佳化

來源:互聯網
上載者:User

偶然在國外一個網站瞅到的,非常的酷,發出來共用一下。一般來說,Python和Java,C#一樣是沒有尾遞迴自動最佳化的能力的,遞迴調用受到調用棧長度的限制被廣泛的詬病,但是這個狂人用一個匪夷所思的方法解決了這個問題並在Python上實現了,從此Python的遞迴調用再也不用受到調用棧長度的制約,太酷了。

首先我們還是從遞迴說起,之前我發過一篇 《淺談遞迴過程以及遞迴的最佳化》其中用到了斐波那契數來作為例子.線性遞迴的演算法由於太過一低效就被我們Pass掉了,我們先來看尾遞過方式的調用:

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)

 

 

這段程式我們來測試一下,調用 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

2 def Fib(n,b1=1,b2=1,c=3):
3     if n<3:
4         return 1
5     else:
6         if n==c:
7             return b1+b2
8         else:
9             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

 

如果有同學出了C#或者Java的實現,請發出來供大家瞻仰,呵呵 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.