我不知道如何使这个尾递归方案函数不再是尾递归。任何人都可以帮助我吗?
(define (foldrecl f x u)
(if (null? x)
u
(foldrecl f (cdr x) (f (car x) u))))
我不知道如何使这个尾递归方案函数不再是尾递归。任何人都可以帮助我吗?
(define (foldrecl f x u)
(if (null? x)
u
(foldrecl f (cdr x) (f (car x) u))))
左折叠是继承迭代的,但您可以通过添加延续轻松使它们递归。例如。
(let ((value expresion-that-calculates))
value)
所以在你的情况下:
(define (foldrecl f x u)
(if (null? x)
u
(let ((result (foldrecl f (cdr x) (f (car x) u))))
result)))
虽然这看起来很有希望,但它并不能保证一个智能的 Scheme 实现会计算出result
刚刚返回的并将其改为尾调用。右折叠更容易,因为它们本质上是递归的:
(define (fold-right proc tail lst)
(if (null? lst)
tail
(proc (car lst)
(fold-right proc tail (cdr lst)))))
在这里,您清楚地看到递归部分需要成为参数cons
,因此除非是基本情况,否则永远不会处于尾部位置。
proc
另请注意,查看调用过程时的参数、结果的尾部tail
和列表参数会稍微简单一些lst
。你甚至不需要阅读我的代码就知道如何使用它,但你的我不知道什么x
和u
ti 并没有帮助参数顺序不遵循fold
Scheme 中已知的任何实现。
递归调用位于尾部位置,因此将其放在另一个过程调用中,如下所示:
(define (identity x) x)
(define (foldrecl f x u)
(if (null? x)
u
(identity (foldrecl f (cdr x) (f (car x) u)))))
现在递归调用不在尾部位置,它不再是尾部递归。
如果编译器知道它什么都不做,但希望它不会做,则允许编译器优化标识函数。
与其做,不如为做做计划;只有在最后,做:
(define (foldreclr f xs a)
(define (go xs)
(if (null? xs)
(lambda (a) a)
(let ((r (go (cdr xs)))) ; first, recursive call;
(lambda ; afterwards, return a plan:
(a) ; given an a, to
(r ; perform the plan for (cdr xs)
(f (car xs) a)))))) ; AFTER processing (car x) and a.
((go xs) ; when the overall plan is ready,
a)) ; use it with the supplied value
内部函数go
遵循正确的折叠模式。它首先进行递归调用,然后才组合并返回一个值,该计划首先将列表的头部元素与累加器值组合,然后执行列表尾部的计划——就像原来的foldrecl
那样。
当整个列表变成行动计划时,最终执行该行动以转换提供的初始累加器值——执行与原始foldrecl
左折叠相同的计算。
这被称为向右倾斜,您再次向左倾斜。(*)
> (foldreclr - (list 1 2 3 4) 0) ; 4-(3-(2-(1-0)))
2
> (foldreclr - (list 4 3 2 1) 0) ; 1-(2-(3-(4-0)))
-2
也可以看看:
(抱歉,这些在 Haskell 中,但 Haskell 也是 Lisp。)