Python開啟尾遞迴最佳化!

來源:互聯網
上載者:User

標籤:

本文和大家分享的主要是python中尾遞迴相關使用方法,希望通過本文的分享能協助大家更好的學習python語言,一起來看看吧。

一般遞迴與尾遞迴

一般遞迴

def normal_recursion(n):

if n == 1:

return 1

else:

return n + normal_recursion(n-1)

執行:

normal_recursion(5)5 + normal_recursion(4)5 + 4 + normal_recursion(3)5 + 4 + 3 + normal_recursion(2)5 + 4 + 3 + 2 + normal_recursion(1)5 + 4 + 3 + 35 + 4 + 65 + 1015

可以看到, 一般遞迴, 每一級遞迴都需要調用函數, 會建立新的棧,

隨著遞迴深度的增加, 建立的棧越來越多, 造成爆棧:boom:

尾遞迴

尾遞迴基於函數的尾調用 , 每一級調用直接返回函數的傳回值更新調用棧,而不用建立新的調用棧, 類似迭代的實現, 時間和空間上均最佳化了一般遞迴!

def tail_recursion(n, total=0):

if n == 0:

return total

else:

return tail_recursion(n-1, total+n)

執行:

tail_recursion(5)tail_recursion(4, 5)tail_recursion(3, 9)tail_recursion(2, 12)tail_recursion(1, 14)tail_recursion(0, 15)15

可以看到, 每一級遞迴的函數調用變成"線性"的形式.

深入理解尾遞迴

呃, 所以呢? 是不是感覺還不夠過癮... 誰說尾遞迴調用就不用建立新的棧呢?

還是讓我們去底層一探究竟吧

int tail_recursion(int n, int total) {

if (n == 0) {

return total;

}

else {

return tail_recursion(n-1, total+n);

}

}

int main(void) {

int total = 0, n = 4;

tail_recursion(n, total);

return 0;

}

反組譯碼

·$ gcc -S tail_recursion.c -o normal_recursion.S

·$ gcc -S -O2 tail_recursion.c -o tail_recursion.S gcc開啟尾遞迴最佳化

對比反組譯碼代碼如下(AT&T文法)


可以看到, 開啟尾遞迴最佳化前, 使用call調用函數, 建立了新的調用棧(LBB0_3);

而開啟尾遞迴最佳化後, 就沒有新的調用棧產生了, 而是直接pop

bp指向的 _tail_recursion 函數的地址(pushq %rbp)然後返回,

仍舊用的是同一個調用棧!

存在的問題

雖然尾遞迴最佳化很好, 但python 不支援尾遞迴,遞迴深度超過1000時會報錯

RuntimeError: maximum recursion depth exceeded

一個牛人想出的解決辦法

實現一個 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 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_optimizeddef factorial(n, acc=1):

"calculate a factorial"

if n == 0:

return acc

return factorial(n-1, n*acc)

print factorial(10000)

為了更清晰的展示開啟尾遞迴最佳化前、後調用棧的變化和tail_call_optimized裝飾器拋異常退出遞迴調用棧的作用, 我這裡利用 pudb調試工具 做了動圖

開啟尾遞迴最佳化前的調用棧


開啟尾遞迴最佳化後(tail_call_optimized裝飾器)的調用棧

通過pudb右側邊欄的stack, 可以很清晰的看到調用棧的變化.

因為尾遞迴沒有調用棧的嵌套, 所以Python也不會報 RuntimeError: maximum recursion depth exceeded 錯誤了!

這裡解釋一下 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 sys

def 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 # 調用者的幀

 

 

來源:SegmentFault



Python開啟尾遞迴最佳化!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.