尾遞歸思考

概念

遞歸是函數(shù)不停的調用自身直到滿足結束條件退出蟆融。遞歸存在一個很大的問題就是隨著遞歸的深入笼裳,占用大量的椙﹂荩空間憎夷,最后很有可能導致棧溢出。
尾遞歸是遞歸的一種特例昧旨。尾遞歸從名字上理解就是把遞歸放到函數(shù)的最后拾给。在尾遞歸中祥得,先執(zhí)行計算的部分,然后把計算結果鸣戴,作為參數(shù)傳入下一次遞歸啃沪。

實例

眾所周知斐波那契數(shù)列是遞歸的一個例子。

遞歸實現(xiàn)

def fibonacci(n):
    if n <= 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

當我們調用fibonacci(1001)的時候窄锅,會拋出下面的異常:

RecursionError: maximum recursion depth exceeded in comparison

從異炒辞В看已經超出了遞歸調用的最大深度,系統(tǒng)的最大深度可以通過sys.getrecursionlimit()獲得入偷,這個設置的默認值是1000追驴。當然我們可以修改這個值達到我們的要求。

尾遞歸實現(xiàn)

def fibonacci(n, b1=1, b2=1, c=3):
    if n < 3:
        return 1
    else:
        if n == c:
            return b1 + b2
        else:
            return fibonacci(n, b1=b2, b2=b1+b2, c=c+1)

尾遞歸的特點就是最后返回的結果就是下次調用返回的結果疏之。
同樣我們也調用fibonacci(1001)殿雪,很遺憾還是拋出了異常:

RecursionError: maximum recursion depth exceeded in comparison

這說明了python沒有對尾遞歸做任何的優(yōu)化。對于尾遞歸來說每次調用的函數(shù)除了參數(shù)的數(shù)值不一樣锋爪,其他完全一樣丙曙,也就是說完全可以僅僅修改一下參數(shù)的數(shù)值利用棧內保存的函數(shù)就能處理。

神奇的裝飾器

既然沒有優(yōu)化其骄,那就沒有其他辦法了嗎亏镰?當然是有的,這里不得不提一下一個神奇的裝飾器@tail_call_optimized拯爽。(參照:http://code.activestate.com/recipes/474088/)

import sys


class TailRecurseException(BaseException):
    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()
        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 as e:
                    args = e.args
                    kwargs = e.kwargs

    func.__doc__ = g.__doc__
    return func

這個裝飾器實現(xiàn)的原理確實精妙索抓,當進行第二次調用的時候就拋出一個異常,把參數(shù)修改一下毯炮,然后返回調用逼肯。這樣就保證了不會有大量的函數(shù)堆積在棧里面。
然后把剛才的尾遞歸修改一下:

@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)

然后再調用一下fibonacci(1001)桃煎,沒有異常了篮幢。

70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501

其他相關

其實對于一些語言是存在尾遞歸優(yōu)化的,比如C語言編譯的時候加優(yōu)化參數(shù) -O2就可以實現(xiàn)备禀。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末洲拇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子曲尸,更是在濱河造成了極大的恐慌赋续,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件另患,死亡現(xiàn)場離奇詭異纽乱,居然都是意外死亡,警方通過查閱死者的電腦和手機昆箕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門鸦列,熙熙樓的掌柜王于貴愁眉苦臉地迎上來租冠,“玉大人,你說我怎么就攤上這事薯嗤⊥绲” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵骆姐,是天一觀的道長镜粤。 經常有香客問我,道長玻褪,這世上最難降的妖魔是什么肉渴? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮带射,結果婚禮上同规,老公的妹妹穿的比我還像新娘。我一直安慰自己窟社,他們只是感情好券勺,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著灿里,像睡著了一般朱灿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钠四,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音跪楞,去河邊找鬼缀去。 笑死,一個胖子當著我的面吹牛甸祭,可吹牛的內容都是我干的缕碎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼池户,長吁一口氣:“原來是場噩夢啊……” “哼咏雌!你這毒婦竟也來了?” 一聲冷哼從身側響起校焦,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤赊抖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寨典,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氛雪,經...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年耸成,在試婚紗的時候發(fā)現(xiàn)自己被綠了报亩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浴鸿。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弦追,靈堂內的尸體忽然破棺而出岳链,到底是詐尸還是另有隱情,我是刑警寧澤劲件,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布掸哑,位于F島的核電站,受9級特大地震影響寇仓,放射性物質發(fā)生泄漏举户。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一遍烦、第九天 我趴在偏房一處隱蔽的房頂上張望俭嘁。 院中可真熱鬧,春花似錦服猪、人聲如沸供填。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽近她。三九已至,卻和暖如春膳帕,著一層夾襖步出監(jiān)牢的瞬間粘捎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工危彩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留攒磨,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓汤徽,卻偏偏與公主長得像娩缰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谒府,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內容

  • 1.函數(shù)參數(shù)的默認值 (1).基本用法 在ES6之前拼坎,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法完疫。
    趙然228閱讀 688評論 0 0
  • 函數(shù)參數(shù)的默認值 基本用法 在ES6之前泰鸡,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法趋惨。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,382評論 0 1
  • 感謝社區(qū)中各位的大力支持鸟顺,譯者再次奉上一點點福利:阿里云產品券,享受所有官網優(yōu)惠,并抽取幸運大獎:點擊這里領取 在...
    HetfieldJoe閱讀 1,811評論 0 14
  • 想要和牛人為伍讯嫂,你也必須成為某一方面的牛人蹦锋。走在什么樣的路上,就會遇見什么樣的人欧芽±虻啵“物以類聚,人以群分千扔≡髅睿”走在教育...
    澠池3112王莉莉閱讀 334評論 3 3
  • 登陸SQL Server (windows身份認證),登陸后右擊曲楚,選擇“屬性”厘唾。 2 左側選擇“安全性”,選中右側...
    DarlingHH閱讀 4,538評論 0 2