python尾遞歸優(yōu)化問題

在思否上面看到了這樣一篇的文章:講述了如何去除python對遞歸的限制,看完后不得不對他佩服,不過仔細(xì)想想也是挺合理的!他主要是通過拋出異常來結(jié)束之前的棧然后新開棧來調(diào)用函數(shù),具體代碼如下:

#!/usr/bin/env python3.5
# 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(Exception):
    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

@tail_call_optimized
def factorial(n, acc=1):
    "calculate a factorial"
    if n == 0:
        return acc
    return factorial(n-1, n*acc)

print(factorial(2000))

具體原理:
當(dāng)遞歸函數(shù)被該裝飾器修飾后, 遞歸調(diào)用在裝飾器while循環(huán)內(nèi)部進(jìn)行, 每當(dāng)產(chǎn)生新的遞歸調(diào)用棧幀時(shí): f.f_back.f_back.f_code == f.f_code:, 就捕獲當(dāng)前尾調(diào)用函數(shù)的參數(shù), 并拋出異常, 從而銷毀遞歸棧并使用捕獲的參數(shù)手動(dòng)調(diào)用遞歸函數(shù). 所以遞歸的過程中始終只存在一個(gè)棧幀對象, 達(dá)到優(yōu)化的目的.

不過這種企業(yè)需求真的很少!在python中遞歸深度最大是950+次(具體多少次真的不好說,不同的機(jī)子和平臺不一樣的!而且同一臺機(jī)子的不同執(zhí)行時(shí)間也不一樣!這里就不去探究他!但是一定小于999次)

思否原文連接https://segmentfault.com/a/1190000007641519

關(guān)于尾遞歸: 當(dāng)遞歸調(diào)用是函數(shù)體中最后執(zhí)行的語句并且它的返回值不屬于表達(dá)式一部分時(shí)稚瘾, 這個(gè)遞歸就是尾遞歸道逗。

現(xiàn)代的編譯器就會(huì)發(fā)現(xiàn)這個(gè)特點(diǎn), 生成優(yōu)化的代碼匕坯, 復(fù)用棧幀蜻底。斐波那契數(shù)列算法中因?yàn)橛袀€(gè)n * factorial(n-1) , 雖然也是遞歸骄崩,但是遞歸的結(jié)果處于一個(gè)表達(dá)式中,還要做計(jì)算, 所以就沒法復(fù)用棧幀了刁赖,只能一層一層的調(diào)用下去搁痛。
雖然尾遞歸調(diào)用也會(huì)創(chuàng)建新的棧, 但是我們可以優(yōu)化使得尾遞歸的每一級調(diào)用共用一個(gè)棧!

關(guān)于尾遞歸優(yōu)化還有另外一種思路,那就是漢諾塔思路:
具體查看網(wǎng)址https://www.cnblogs.com/tgycoder/p/6063722.html

相對于上面的算法,漢諾塔思路會(huì)更加好!但是漢諾塔思路設(shè)計(jì)的東西不會(huì)無限遞歸,雖然他和上面的拋出異常的方式都是尾遞歸的每一級調(diào)用共用一個(gè)棧,但是拋出異常的方式利用了cpython的機(jī)制 "漏洞" 來實(shí)現(xiàn)自己無限遞歸的運(yùn)作方式!但是漢諾塔思路沒利用!

其中一種尾遞歸優(yōu)化的方式其實(shí)就是建立在漢諾塔的思路上面的!比如下面的代碼:

def tail_recursion(n, total=0):
    if n == 0:
        return total
    else:
        return tail_recursion(n-1, total+n)##這一步必須沒有表達(dá)式,而是只有call本身函數(shù)

print(tail_recursion(993))##但是還是受到了cpython解釋器的限制!這里把993更改
                          ##為999會(huì)發(fā)現(xiàn)拋出遞歸最大深度的異常

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末长搀,一起剝皮案震驚了整個(gè)濱河市宇弛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌源请,老刑警劉巖枪芒,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谁尸,居然都是意外死亡舅踪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門良蛮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抽碌,“玉大人,你說我怎么就攤上這事决瞳』踽悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵皮胡,是天一觀的道長痴颊。 經(jīng)常有香客問我,道長屡贺,這世上最難降的妖魔是什么蠢棱? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮甩栈,結(jié)果婚禮上泻仙,老公的妹妹穿的比我還像新娘。我一直安慰自己量没,他們只是感情好玉转,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著允蜈,像睡著了一般冤吨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饶套,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天漩蟆,我揣著相機(jī)與錄音,去河邊找鬼妓蛮。 笑死怠李,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捺癞,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼夷蚊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了髓介?” 一聲冷哼從身側(cè)響起惕鼓,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唐础,沒想到半個(gè)月后箱歧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡一膨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年呀邢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豹绪。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡价淌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出瞒津,到底是詐尸還是另有隱情蝉衣,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布仲智,位于F島的核電站买乃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钓辆。R本人自食惡果不足惜剪验,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望前联。 院中可真熱鬧功戚,春花似錦、人聲如沸似嗤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烁落。三九已至乘粒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伤塌,已是汗流浹背灯萍。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留每聪,地道東北人旦棉。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓齿风,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绑洛。 傳聞我的和親對象是個(gè)殘疾皇子救斑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容