如果乙個函式中所有遞迴形式的呼叫都出現在函式的末尾,我們稱這個遞迴函式是尾遞迴的。當遞迴呼叫是整個函式體中最後執行的語句且它的返回值不屬於表示式的一部分時,這個遞迴呼叫就是尾遞迴。尾遞迴函式的特點是在回歸過程中不用做任何操作,這個特性很重要,因為大多數現代的編譯器會利用這種特點自動生成優化的**。
只要理解了函式呼叫棧,那麼尾遞迴就很好理解了。如果你還不理解什麼叫函式呼叫棧
那麼請看這篇博文。
接下來看乙個典型的遞迴函式:求階乘
***ct的幀如下圖所示:(過程就是指函式)
這個遞迴函式***ct(
n)由於語句
n****ct
(n-1
)的存在,在呼叫
***ct
(n-1
)之後還要用到
***ct(n
)裡的乙個變數
n,所以就必須在***ct(n
)裡儲存
n,所以就必須為
***ct(n
)儲存乙個幀,同理,必須為***ct(
n-1)
,***ct(
n-2)
***ct
(n-3
)........***ct(1
)儲存乙個幀。由此可以看出
+++++++
》如果n
過大的話,那麼儲存的幀就會過多,而我們知道乙個程式獲得的程式棧大小是有限的,所以在執行過程中就很有可能會爆棧。。。爆棧。。。。。
讓我們好好想一下:如果乙個函式(呼叫者)在呼叫另乙個函式(被呼叫者)時,呼叫者不需要儲存任何變數,那麼我們是不是就可以把被呼叫者的幀建立在呼叫者的幀上,即通過覆蓋呼叫者的幀而不是開拓新的空間,來達到節省棧空間的目的呢,這樣,不就大大降低了爆棧的機率麼
說幹就幹,讓我們改寫上面的遞迴函式吧:
仔細觀察上面的函式,我們會發現***ct(
n)函式裡並不需要儲存什麼變數,所以就沒必要儲存***ct(
n)的幀,這裡的關鍵就在於把結果當做乙個引數傳給
***ct
(n-1
)所以我們可以通過覆蓋呼叫者的幀來建立被呼叫者的幀,從而達到節約棧空間的目的。哈!這不就是所謂的尾遞迴嗎(請回到這篇文章的開頭好好體會一下,體會那種豁然開朗的感覺 )
我的上篇博文裡不是有esp ,
ebp兩個東東嗎,你難道沒有發現程式並沒有分配空間來儲存
esp,
ebp兩個「指標麼」。
esp,
ebp就是暫存器之一,以前的
cpu一共有八個暫存器,顧名思義,暫存器就是臨時存放變數的東西,如果引數不是很多的情況下就可以將函式引數儲存在暫存器裡。現在的
cpu暫存器翻倍了,有16
個通用暫存器了,其中
6個可以用來存放引數,所以***,至於超過六個嘛,很抱歉,樓主對編譯原理還不是很清楚,不知道寫編譯器的人會怎麼處理(我估計還是能實現尾遞迴優化)我再給說一下:我們的程式棧是存放在儲存區里(也就是記憶體)暫存器和記憶體是不同的兩種緩衝區。
尾遞迴優化
尾遞迴就是遞迴語句在函式最後執行,且無需對返回值進行進一步操作。編譯器會對這種遞迴進行優化,在進入深層遞迴時候,不是在遞迴棧進行入棧操作,而是直接覆蓋棧頂。線性遞迴與尾遞迴區別如下 線性遞迴 1 2 3 4 5 longrescuvie longn 尾遞迴 1 2 3 4 5 6 7 8 9 10 ...
尾遞迴優化
什麼是尾遞迴 尾遞迴就將遞迴呼叫寫在函式的尾部return 尾遞迴的好處 解決傳統遞迴的棧溢位問題 尾遞迴適合的業務場景 1.需要遞迴優化的函式沒有用timeout等非同步佇列進行遞迴呼叫函式自己 2.需要遞迴優化的遞迴函式的返回值不是每次都返回,而是條件性返回 尾遞迴優化後的遞迴demo meth...
python遞迴 Python 與尾遞迴優化
有很多時候,使用遞迴的方式寫 要比迭代更直觀一些,以下面的階乘為例 def factorial n if n 0 return 1 return factorial n 1 n 但是這個函式呼叫,如果展開,會變成如下的形式 factorial 4 factorial 3 4 factorial 2 ...