3 回答
TA贡献1828条经验 获得超3个赞
尾部调用优化可以避免为函数分配新的堆栈框架,因为调用函数只会返回从被调用函数获得的值。最常见的用法是尾递归,其中为利用尾调用优化而编写的递归函数可以使用常量堆栈空间。
方案是少数几种在规范中保证任何实现都必须提供这种优化的编程语言之一。(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))
第一个函数不是尾递归函数,因为在进行递归调用时,函数需要跟踪调用返回后与结果有关的乘法。因此,堆栈看起来如下:
(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)
添加回答
举报