如何使此Scheme函数不尾递归?

时间:2019-09-26 22:41:00

标签: recursion scheme tail-recursion fold foldleft

我不知道如何使该尾部递归Scheme函数不再是尾部递归。有人可以帮助我吗?

(define (foldrecl f x u)
  (if (null? x)
      u 
      (foldrecl f (cdr x) (f (car x) u))))

3 个答案:

答案 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会更简单。您甚至不需要阅读我的代码即可知道如何使用它,但是您不知道xu是什么,并且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。)