Scheme中的递归函数是否总是尾调用优化?

时间:2011-02-17 20:22:38

标签: scheme tail-call-optimization

我已经阅读了有关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)))))

2 个答案:

答案 0 :(得分:10)

考虑尾调用的一种有用方法是询问“递归过程调用的结果必然会发生什么?”

第一个函数不能进行尾部优化,因为必须使用fac内部调用的结果,并乘以n以产生结果总体呼叫fac

然而,在第二种情况下,对fact的“外部”调用的结果是......内部调用fact的结果。没有必要对它进行任何操作,后一个值可以直接作为函数的值传回。这意味着不必保留其他函数上下文,因此可以简单地将其丢弃。

R5RS标准使用tail context的概念来定义'尾调用',这基本上就是我上面所描述的。

答案 1 :(得分:4)

不,第一个fac无法优化。

调用函数时,您需要知道调用函数的位置,以便在调用完成后返回到该位置,并在将来的计算中使用调用的结果(fac功能)。

fact的实现方式不同。 fact做的最后一件事就是自称。没有必要记住我们正在调用的地方 - 相反,我们可以执行尾部调用消除。在fact返回后,没有其他操作应该完成。