知识点讲解八:Python中的尾递归
尾遞歸
**尾遞歸的原理:**當編譯器檢測到一個函數調用是尾遞歸的時候,它就覆蓋當前的活動記錄而不是在棧中去創建一個新的。編譯器可以做到這點,因為遞歸調用是當前活躍期內最后一條待執行的語句,于是當這個調用返回時棧幀中并沒有其他事情可做,因此也就沒有保存棧幀的必要了。通過覆蓋當前的棧幀而不是在其之上重新添加一個,這樣所使用的棧空間就大大縮減了,這使得實際的運行效率會變得更高。
換一種說法,尾遞歸是指,在函數返回的時候,調用自身本身,并且,return語句不能包含表達式。這樣,編譯器或者解釋器就可以把尾遞歸做優化,使遞歸本身無論調用多少次,都只占用一個棧幀,不會出現棧溢出的情況。
def facttail(int n, int res) {if (n < 0)return 0;else if(n == 0)return 1;else if(n == 1)return res;elsereturn facttail(n - 1, n *res); }需要注意的是python 不支持尾遞歸,遞歸深度超過1000時會報錯,故此需要我們做一些處理來解決這個問題。
尾遞歸優化
通過實現一個 tail_call_optimized 裝飾器,來優化尾遞歸。
#!/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 sysclass TailRecurseException:def __init__(self, args, kwargs):self.args = argsself.kwargs = kwargsdef tail_call_optimized(g):"""This function decorates a function with tail calloptimization. It does this by throwing an exceptionif it is it's own grandparent, and catching suchexceptions to fake the tail call optimization.This function fails if the decoratedfunction 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, e:args = e.argskwargs = e.kwargsfunc.__doc__ = g.__doc__return func@tail_call_optimized def factorial(n, acc=1):"calculate a factorial"if n == 0:return accreturn factorial(n-1, n*acc)print factorial(10000)這里解釋一下sys._getframe()函數:
sys._getframe([depth]):
Return a frame object from the call stack.
If optional integer depth is given, return the frame object that many calls below the top of the stack.
If that is deeper than the call stack, ValueEfror is raised. The default for depth is zero,
returning the frame at the top of the call stack.
即返回depth深度調用的棧幀對象.
import sysdef get_cur_info():print sys._getframe().f_code.co_filename # 當前文件名print sys._getframe().f_code.co_name # 當前函數名print sys._getframe().f_lineno # 當前行號print sys._getframe().f_back # 調用者的幀tail_call_optimized實現尾遞歸優化的原理: 當遞歸函數被該裝飾器修飾后, 遞歸調用在裝飾器while循環內部進行, 每當產生新的遞歸調用棧幀時: f.f_back.f_back.f_code == f.f_code:, 就捕獲當前尾調用函數的參數, 并拋出異常, 從而銷毀遞歸棧并使用捕獲的參數手動調用遞歸函數. 所以遞歸的過程中始終只存在一個棧幀對象, 達到優化的目的。
關于裝飾器的相關知識可以參考這篇博客:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000
參考文章:https://blog.csdn.net/jingtiangao/article/details/51554889
https://blog.csdn.net/zcyzsy/article/details/77151709
https://segmentfault.com/a/1190000007641519
總結
以上是生活随笔為你收集整理的知识点讲解八:Python中的尾递归的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 知识点讲解四:栈溢出(stack ove
- 下一篇: websocket python爬虫_p