1. 程式人生 > >【Python學習筆記】一個很酷的尾遞迴優化

【Python學習筆記】一個很酷的尾遞迴優化

一般來說,Python、Java和C#是沒有尾遞迴優化能力的。
用斐波那契數列舉栗子

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)

>>> Fib(1001)
70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501L

如果我們用1002進行測試,會出現以下結果

>>> Fib(1002)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  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 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 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)

再進行以下1002的測試

>>> factorial(1002)
403595226631845209051880628629640843684504427553075197870696397761049187812849704911861525200847286953691029536005008041199679136947538974195145603806613764659598772448343155346099936257625753094803884027860163939789842138835459726309735765486625072922145181887975022572105212827325352650360822643468087741771243274550281589906300986381710232456423433251607344294784039373634656024124453620089721583696043568454453734050864695625360160060837985907843886863299796154720279007113956951303885722179881815749987309805714978843425686190000118314099658179076045908112174184036411692279009131566184678468723846725908145293854399345046411198447956200779858905167173352465714153538801211087720100276119910066915976203462207958828341534221899791288298092817195947660859191130400508223302612192464542972486137970999130314487401586333890803098965666729069983059073029836289064949751403828279759468153435153751780866336232529843901200433684288385627283533199027396434893357282086051585816529002990308480430936068459723972706109774049015004995093607154690663050562540125426199747069929118955699230774847071199289100876508706102549187041138413686787948205954112870737405031681121042921061995174289468425331650464561340409424173780188880812336110785337046525095105430858497742434742094044183974861014953066385473893642483322068557624990042622887596537142678944093257718557160897828029849892850965669582034472885502604911928460284005306342333130336198553219540604482194933412078382116571387810188247661856673784645931148063936059156551998852938063173052063949026304605205631624638774310744480832995609830297494545245656244455620251778253774769266089821112470989893912601073959708491868875645584730271480394263197569955225062667191096707656654407022887525790856785877899038650115933178051995456774627931040674690671748424144553319969406033752761076645509257674502108335781092379493678262981271954245666983743955528481743109188752583996172412523833843658095508570036882989691250736327439493831143034902758721795187227924667136887444201148357041223132757556636766608468460560883010885098435917684827483525679451551727590758306091652499677082687155106869995981028480229123981226964327190673310250990586533694909587495431670564164338201999882039484224426801780967555634548300999593123533545305689646564984159483631997655252944785081759776787922944000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000L

【得意臉】

好吧,這是我從國外的一個網上拉下來的。。。直接貼上

import sys    
class TailRecurseException:  
   def __init__(self, args, kwargs):  
     self.args = args  
     self.kwargs = kwargs  

def tail_call_optimized(g):   
   def func(*args, **kwargs):  
     f = sys._getframe()  
     if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code:  
       raise TailRecurseException(args, kwargs)  
     else:  
       while 1:  
         try:  
           return g(*args, **kwargs)  
         except TailRecurseException, e:  
           args = e.args  
           kwargs = e.kwargs  
   func.__doc__ = g.__doc__  
   return func  

使用的方法前面已經展示了,令我感到大開眼界的是,作者用了丟擲異常然後自己捕獲的方式來打破呼叫棧的增長,簡直是太匪夷所思了。而且效率問題,和直接尾遞迴Fib相比大概造成了五倍的時間開銷。
最後很不可思議的,尾遞迴優化的目的達成了。