4 回答

胡子哥哥
TA貢獻1825條經驗 獲得超6個贊
考慮一個添加前N個整數的簡單函數。(例如sum(5) = 1 + 2 + 3 + 4 + 5 = 15
)。
這是一個使用遞歸的簡單JavaScript實現:
function recsum(x) { if (x===1) { return x; } else { return x + recsum(x-1); }}
如果你打電話recsum(5)
,這就是JavaScript解釋器會評估的內容:
recsum(5)5 + recsum(4)5 + (4 + recsum(3))5 + (4 + (3 + recsum(2)))5 + (4 + (3 + (2 + recsum(1))))5 + (4 + (3 + (2 + 1)))15
請注意每個遞歸調用必須在JavaScript解釋器開始實際執行計算總和之前完成。
這是同一函數的尾遞歸版本:
function tailrecsum(x, running_total=0) { if (x===0) { return running_total; } else { return tailrecsum(x-1, running_total+x); }}
這是您調用時將發生的事件序列tailrecsum(5)
(tailrecsum(5, 0)
由于默認的第二個參數,這將是有效的)。
tailrecsum(5, 0)tailrecsum(4, 5)tailrecsum(3, 9)tailrecsum(2, 12)tailrecsum(1, 14)tailrecsum(0, 15)15
在尾遞歸的情況下,每次遞歸調用的評估running_total
都會更新。
注意:原始答案使用了Python中的示例。這些已經改為JavaScript,因為現代JavaScript解釋器支持尾調用優化,但Python解釋器不支持。

嚕嚕噠
TA貢獻1784條經驗 獲得超7個贊
重要的一點是尾遞歸基本上等于循環。這不僅僅是編譯器優化的問題,而是表達性的基本事實。這有兩種方式:你可以采取任何形式的循環
while(E) { S }; return Q
where E
和Q
是表達式,S
是一系列語句,并將其轉換為尾遞歸函數
f() = if E then { S; return f() } else { return Q }
當然,E
,S
,和Q
必須定義來計算對一些變量的一些有趣的價值。例如,循環函數
sum(n) { int i = 1, k = 0; while( i <= n ) { k += i; ++i; } return k;}
相當于尾遞歸函數
sum_aux(n,i,k) { if( i <= n ) { return sum_aux(n,i+1,k+i); } else { return k; }}sum(n) { return sum_aux(n,1,0);}
(這種帶有參數較少的函數的尾遞歸函數的“包裝”是一種常見的功能習慣。)
添加回答
舉報
0/150
提交
取消