3 回答

TA貢獻1828條經驗 獲得超3個贊
尾部調用優化可以避免為函數分配新的堆??蚣埽驗檎{用函數只會返回從被調用函數獲得的值。最常見的用法是尾遞歸,其中為利用尾調用優化而編寫的遞歸函數可以使用常量堆??臻g。
方案是少數幾種在規范中保證任何實現都必須提供這種優化的編程語言之一。(JavaScript也是這樣,從ES6開始),下面是方案中階乘函數的兩個示例:
(define (fact x)
(if (= x 0) 1
(* x (fact (- x 1)))))
(define (fact x)
(define (fact-tail x accum)
(if (= x 0) accum
(fact-tail (- x 1) (* x accum))))
(fact-tail x 1))
第一個函數不是尾遞歸函數,因為在進行遞歸調用時,函數需要跟蹤調用返回后與結果有關的乘法。因此,堆??雌饋砣缦拢?/p>
(fact 3)
(* 3 (fact 2))
(* 3 (* 2 (fact 1)))
(* 3 (* 2 (* 1 (fact 0))))
(* 3 (* 2 (* 1 1)))
(* 3 (* 2 1))
(* 3 2)
6
相反,尾遞歸階乘的堆棧跟蹤如下所示:
(fact 3)
(fact-tail 3 1)
(fact-tail 2 3)
(fact-tail 1 6)
(fact-tail 0 6)
6
正如您所看到的,我們只需要跟蹤對事實尾的每一次調用的相同數據量,因為我們只是簡單地將得到的值直接返回到頂部。這意味著,即使我打電話給(事實1000000),我只需要與(事實3)相同的空間。非尾遞歸事實并非如此,因此較大的值可能會導致堆棧溢出。

TA貢獻1815條經驗 獲得超10個贊
unsigned fac(unsigned n){ if (n < 2) return 1; return n * fac(n - 1);}
fac()
unsigned fac(unsigned n){ if (n < 2) return 1; unsigned acc = fac(n - 1); return n * acc;}
fac()
unsigned fac(unsigned n){ return fac_tailrec(1, n);}unsigned fac_tailrec(unsigned acc, unsigned n){ if (n < 2) return acc; return fac_tailrec(n * acc, n - 1);}
unsigned fac_tailrec(unsigned acc, unsigned n){TOP: if (n < 2) return acc; acc = n * acc; n = n - 1; goto TOP;}
fac()
unsigned fac(unsigned n){ unsigned acc = 1;TOP: if (n < 2) return acc; acc = n * acc; n = n - 1; goto TOP;}
unsigned fac(unsigned n){ unsigned acc = 1; for (; n > 1; --n) acc *= n; return acc;}

TA貢獻1797條經驗 獲得超6個贊
def fact(n): if n == 0: return 1 return n * fact(n-1)
def fact_h(n, acc): if n == 0: return acc return fact_h(n-1, acc*n)def fact(n): return fact_h(n, 1)
添加回答
舉報