計劃要求淘汰尾數。不是tail調用遞歸的代碼將需要額外的堆棧框架。
讓我們假設javascript支持尾部調用優化,這些函數定義中的第二個將僅使用1個堆棧幀,而第一個由於+
將需要額外的堆棧幀。
function sum(n) {
if (n === 0)
return n;
return n + sum(n - 1);
}
function sum(n) {
function doSum(total, n) {
if (n === 0)
return total;
return doSum(total + n, n - 1);
}
return doSum(0, n);
}
許多遞歸函數可以尾調用優化通過把計算的結果在棧上
概念調用的第一個定義這個樣子的
3 + sum(2)
3 + sum(2) = 3 + 2 + sum(1)
3 + sum(2) = 3 + 2 + sum(1) = 3 + 2 + 1 + sum(0)
3 + sum(2) = 3 + 2 + sum(1) = 3 + 2 + 1 + sum(0) = 3 + 2 + 1 + 0
3 + sum(2) = 3 + 2 + sum(1) = 3 + 2 + 1 + sum(0) = 6
3 + sum(2) = 3 + 2 + sum(1) = 6
3 + sum(2) = 6
6
調用的寫入第二個定義看起來像這樣
sum(3, sum(2)) = sum(5, sum(1)) = sum(6, sum(0)) = 6
當你說:「雖然第一個,由於+需要一個額外的堆棧框架」在任何時候只有一個額外的堆棧框架? C爲每個函數調用添加了一個新的堆棧框架,我相信這樣做是否意味着Scheme中的遞歸更有效?如果我錯過了什麼,請告訴我;這只是一個奇怪的想法,我不能在明年之前採取任何深入的課程。謝謝你的方式真棒回答。 –
尾部調用優化是編譯器的魔力。 Scheme標準要求編譯器實現它。使用跳轉指令而不是呼叫實現尾部呼叫優化。尾調用優化遞歸因此更有效。 – Bhaskar
啊,所以如果沒有尾遞歸(例如在第一個函數中),那麼Scheme中遞歸的效率在C中是相同的? –