我不知道如何使该尾部递归Scheme函数不再是尾部递归。有人可以帮助我吗?
(define (foldrecl f x u)
(if (null? x)
u
(foldrecl f (cdr x) (f (car x) u))))
答案 0 :(得分:1)
向左折叠是继承的迭代,但是您可以通过添加连续来轻松地使它们递归。例如。
(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并不能确保参数顺序不遵循任何顺序Scheme中的fold
个实现。
答案 1 :(得分:1)
递归调用位于尾部位置,因此将其放在另一个过程调用中,如下所示:
"
现在递归调用不在尾部位置,它不再是尾部递归。
如果编译器知道它什么也不做,但希望它不会,则允许优化身份函数。
答案 2 :(得分:0)
而不是这样做,而是要制定一个计划;最后,要做:
(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
遵循右折叠模式。它首先进行递归调用,然后才进行组合并返回一个值,首先计划 将该列表的head元素与 accumulator 值结合,然后 为列表的尾部执行计划-就像原始的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。)