尾遞歸即在遞歸函數中不使用新的變量,經過在函數中傳參的形式傳遞變量python
尾遞歸基於函數的尾調用, 每一級調用直接返回函數的返回值更新調用棧,而不用建立新的調用棧, 相似迭代的實現, 時間和空間上均優化了通常遞歸!函數
把計算出的值存在函數內部(固然不止尾遞歸)是其計算方法,從而不用在棧中去建立一個新的,這樣就大大節省了空間。函數調用中最後返回的結果是單純的遞歸函數調用(或返回結果)就是尾遞歸優化
推薦博客:https://blog.csdn.net/qq_39521554/article/details/80112748this
尾遞歸其實就是while循環的一種變形,全部能用尾遞歸寫出來的均可以用while循環寫出來.net
斐波那契數code
普通遞歸blog
def Fib(n): if n<3: return 1 else: return Fib(n-1) + Fib(n-2)
尾遞歸遞歸
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)
因爲python解釋器對尾遞歸沒有作優化,因此這樣棧的使用仍是沒有減小,下面使用裝飾器get
@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)
裝飾器代碼博客
#!/usr/bin/env python2.4 # This program shows off a python decorator( # which implements tail call optimization. It # does this by throwing an exception if it is # it's own grandparent, and catching such # exceptions to recall the stack. import sys class TailRecurseException: def __init__(self, args, kwargs): self.args = args self.kwargs = kwargs def tail_call_optimized(g): """ This function decorates a function with tail call optimization. It does this by throwing an exception if it is it's own grandparent, and catching such exceptions to fake the tail call optimization. This function fails if the decorated function recurses in a non-tail context. """ def func(*args, **kwargs): f = sys._getframe() # 爲何是grandparent, 函數默認的第一層遞歸是父調用, # 對於尾遞歸, 不但願產生新的函數調用(即:祖父調用), # 因此這裏拋出異常, 拿到參數, 退出被修飾函數的遞歸調用棧!(後面有動圖分析) 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 @tail_call_optimized def factorial(n, acc=1): "calculate a factorial" if n == 0: return acc return factorial(n-1, n*acc) print factorial(10000)
思考:若是尾遞歸中的參數中傳遞一個函數的地址,是否可行,是否會減小邏輯