以Scheme中的一个右折叠函数为例,以下是一个天真的实现:
现在,
但是,如果左折叠可以在常量空间(减去正在累加的值)中运行,而列表上的右折叠与其反转后的左折叠相同,那么我想应该有一种方法来实现一个尾递归的右折叠,它也可以在常量空间中运行。如果有的话,我该怎么做呢?更具体地说,有哪些方法可以将非尾递归函数转换为尾递归函数,最好是可以在常量空间中运行(假设在命令式语言中可以编写显式循环,该循环也可以在常量空间中运行)?我是否有任何错误的假设?
虽然我标记了 Scheme 和 Lisp,但我对任何语言的解决方案都感兴趣;这些方法应该适用于一般的函数式程序。
(define (fold-rite kons knil clist)
(if (null? clist)
knil
(kons (car clist) (fold-rite kons knil (cdr clist)))))
这显然不是尾调用优化的候选者,因为在调用fold-rite
之前必须完成递归调用,才能调用kons
。现在,我可能会聪明一点,改用续延传递风格:
(define (fold-rite-cps kons knil clist kontinuation)
(if (null? clist)
(kontinuation knil)
(fold-rite-cps kons knil (cdr clist)
(lambda (x) (kontinuation (kons (car clist) x))))))
现在,
fold-rite-cps
是尾递归的,但在递归过程中建立的中间 continuation 仍然需要被存储在某个地方。因此,虽然我可能不会爆栈,但我仍然使用了与第一个版本相同的内存量。而且我总觉得,先收集大量的 continuations,然后一下子展开它们应该对性能不利,尽管第二个版本在我的计算机上实际上要快得多。但是,如果左折叠可以在常量空间(减去正在累加的值)中运行,而列表上的右折叠与其反转后的左折叠相同,那么我想应该有一种方法来实现一个尾递归的右折叠,它也可以在常量空间中运行。如果有的话,我该怎么做呢?更具体地说,有哪些方法可以将非尾递归函数转换为尾递归函数,最好是可以在常量空间中运行(假设在命令式语言中可以编写显式循环,该循环也可以在常量空间中运行)?我是否有任何错误的假设?
虽然我标记了 Scheme 和 Lisp,但我对任何语言的解决方案都感兴趣;这些方法应该适用于一般的函数式程序。