(define (plus x y)
(if (zero? y) x
(add1 (plus x (sub1 y)))))
当y != 0
它必须记住,一旦(plus x (sub1 y))
知道结果,它就必须对其进行计算add1
。因此,当 y 达到零时,递归处于最深处。现在回溯阶段开始并add1
执行 's。可以使用 观察此过程trace
。
我做了跟踪:
(require racket/trace)
(define (add1 x) ...)
(define (sub1 x) ...)
(define (plus x y) ...)
(trace plus)
(plus 2 3)
这是跟踪:
>(plus 2 3)
> (plus 2 2)
> >(plus 2 1)
> > (plus 2 0) // Deepest point of recursion
< < 2 // Backtracking begins, performing add1 on the results
< <3
< 4
<5
5 // Result
不同的是,另一个版本没有回溯阶段。它会调用自己几次,但它是迭代的,因为它正在记住中间结果(作为参数传递)。因此,该过程不会消耗额外的空间。
有时实现尾递归过程比编写它的迭代等价物更容易或更优雅。但是出于某些目的,您不能/可能不会以递归方式实现它。
PS:我有一堂课讲了一些关于垃圾收集算法的内容。这样的算法可能不是递归的,因为可能没有剩余空间,因此没有空间用于递归。我记得一种叫做“Deutsch-Schorr-Waite”的算法,一开始真的很难理解。首先他实现了递归版本只是为了理解这个概念,然后他编写了迭代版本(因此必须手动记住他从哪里来),相信我递归版本更容易但不能在实践中使用......