原因很多人的都知道,讓我們先回顧一下函數調用的大概過程:

堅守“ 做人真誠 · 做事靠譜 · 口碑至上 · 高效敬業 ”的價值觀,專業網站建設服務10余年為成都成都鑿毛機小微創業公司專業提供成都企業網站定制營銷網站建設商城網站建設手機網站建設小程序網站建設網站改版,從內容策劃、視覺設計、底層架構、網頁布局、功能開發迭代于一體的高端網站建設服務。
1)調用開始前,調用方(或函數本身)會往棧上壓相關的數據,參數,返回地址,局部變量等。
2)執行函數。
3)清理棧上相關的數據,返回。
因此,在函數 A 執行的時候,如果在第二步中,它又調用了另一個函數 B,B 又調用 C.... 棧就會不斷地增長不斷地裝入數據,當這個調用鏈很深的時候,棧很容易就滿 了,這就是一般遞歸函數所容易面臨的大問題。
而尾遞歸在某些語言的實現上,能避免上述所說的問題,注意是某些語言上,尾遞歸本身并不能消除函數調用棧過長的問題,那什么是尾遞歸呢?在上面寫的一般遞歸函數 func() 中,我們可以看到,func(n) 是依賴于 func(n-1) 的,func(n) 只有在得到 func(n-1) 的結果之后,才能計算它自己的返回值,因此理論上,在 func(n-1) 返回之前,func(n),不能結束返回。因此func(n)就必須保留它在棧上的數據,直到func(n-1)先返回,而尾遞歸的實現則可以在編譯器的幫助下,消除這個限制
TCO,tail-call optimization,其實有多種解讀方式。
最常見的解讀方式是:對于尾調用的函數調用,不要浪費棧空間,而要復用調用者的棧空間。這樣的結果就是一長串尾調用不會爆棧,而沒有TCO的話同樣的調用就會爆棧。
從這個意義上說,題主貼的那個recipe確實達到了TCO的部分目的:
通過stack introspection查看調用鏈上的調用者之中有沒有自己
有的話,通過拋異常來迫使棧回退(stack unwind)到之前的一個自己的frame
在回退到的frame接住異常,拿出后來調用的參數,用新參數再次調用自己
這樣就可以讓尾遞歸不爆棧。但這樣做性能是沒保證的…而且對于完全沒遞歸過的一般尾調用也不起作用。
一種對TCO的常見誤解是:由編譯器或運行時系統把尾調用/尾遞歸實現得很快。這不是TCO真正要強調的事情——不爆棧才是最重要的。也就是說其實重點不在“優化”,而在于“尾調用不爆棧”這個語義保證。
“proper tail-call”的叫法遠比“tail-call optimization”來得合適。
因而像題主說的那種做法,可以算部分TCO,但算不上“性能優化”意義上的優化。
遞歸的思想主要是能夠重復某些動作,比如簡單的階乘,次方,回溯中的八皇后,數獨,還有漢諾塔,分形。
由于堆棧的機制,一般的遞歸可以保留某些變量在歷史狀態中,比如你提到的return
x
*
power...,
但是某些或許龐大的問題或者是深度過大的問題就需要盡量避免遞歸,因為可能會棧溢出。還有一個問題是~python不支持尾遞歸優化!!!!所以~還是盡量避免遞歸的出現。
def
power(x,
n)
if
n
0:
return
1
return
x
*
power(x,
n
-
1)
power(3,
3)
3
*
power(3,
2)
3
*
(3
*
power(3,
1))
3
*
(3
*
(3
*
power(3,
0)))
3
*
(3
*
(3
*
1))
這里n
=
0,
return
1
3
*
(3
*
3)
3
*
9
27
當函數形參n=0的時候,開始回退~直到第一次調用power結束。
遞歸式方法可以被用于解決很多的計算機科學問題,因此它是計算機科學中十分重要的一個概念。
絕大多數編程語言支持函數的自調用,在這些語言中函數可以通過調用自身來進行遞歸。計算理論可以證明遞歸的作用可以完全取代循環,因此在很多函數編程語言(如Scheme)中習慣用遞歸來實現循環。
計算機科學家尼克勞斯·維爾特如此描述遞歸:
遞歸的強大之處在于它允許用戶用有限的語句描述無限的對象。因此,在計算機科學中,遞歸可以被用來描述無限步的運算,盡管描述運算的程序是有限的。
python 2 遞歸函數和其它語言,基本沒有差別,只是不支持尾遞歸。無限遞歸最大值為固定的,但可以修改。
作者:黃哥
Python本身是不支持尾遞歸的(via),并且對遞歸次數有限制的,當遞歸次數超過1000次的時候,就會拋出“RuntimeError: maximum recursion depth exceeded”異常。
下面是筆者的個人理解: 把計算出的值存在函數內部(當然不止尾遞歸)是其計算方法,從而不用在棧中去創建一個新的,這樣就大大節省了空間。函數調用中最后返回的結果是單純的遞歸函數調用(或返回結果)就是尾遞歸。
實例還是和筆者的上一篇文章相同,建議讀者閱讀 Python —— 遞歸
常規遞歸階乘:
我們來看一下執行過程:
但是如果把上面的函數寫成如下形式:
我們再看下執行過程:
很直觀的就可以看出,這次的 factorial 函數在遞歸調用的時候不會產生一系列逐漸增多的中間變量了,而是將狀態保存在 acc 這個變量中。而這種形式的遞歸,就叫做尾遞歸。
常規遞斐波那契數列:
而尾遞歸:
一下子就充滿了逼格,還高效了許多,何樂而不為呢!
當前名稱:python尾遞歸函數 python里的遞歸函數
瀏覽地址:http://www.yijiale78.com/article36/hihssg.html
成都網站建設公司_創新互聯,為您提供企業網站制作、品牌網站設計、關鍵詞優化、App設計、商城網站、自適應網站
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯