尾遞歸

尾遞歸即在遞歸函數中不使用新的變量,經過在函數中傳參的形式傳遞變量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)
 

  

思考:若是尾遞歸中的參數中傳遞一個函數的地址,是否可行,是否會減小邏輯

相關文章
相關標籤/搜索