我已经阅读了有关Scheme中尾调优化的内容。但我不确定我是否理解尾调用的概念。如果我有这样的代码:
(define (fac n)
(if (= n 0)
1
(* n (fac (- n 1)))))
这可以优化,这样就不会占用堆栈内存吗? 或者尾部调用优化只能应用于这样的函数:
(define (factorial n)
(let fact ([i n] [acc 1])
(if (zero? i)
acc
(fact (- i 1) (* acc i)))))
答案 0 :(得分:10)
考虑尾调用的一种有用方法是询问“递归过程调用的结果必然会发生什么?”
第一个函数不能进行尾部优化,因为必须使用fac
内部调用的结果,并乘以n
以产生结果总体呼叫fac
。
然而,在第二种情况下,对fact
的“外部”调用的结果是......内部调用fact
的结果。没有必要对它进行任何操作,后一个值可以直接作为函数的值传回。这意味着不必保留其他函数上下文,因此可以简单地将其丢弃。
R5RS标准使用tail context的概念来定义'尾调用',这基本上就是我上面所描述的。
答案 1 :(得分:4)
不,第一个fac
无法优化。
调用函数时,您需要知道调用函数的位置,以便在调用完成后返回到该位置,并在将来的计算中使用调用的结果(fac
功能)。
fact
的实现方式不同。 fact
做的最后一件事就是自称。没有必要记住我们正在调用的地方 - 相反,我们可以执行尾部调用消除。在fact
返回后,没有其他操作应该完成。