如何使这个 Scheme 函数不尾递归?

How to make this Scheme function not tail recursive?

我不知道如何让这个尾递归 Scheme 函数不再是尾递归。谁能帮帮我?

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

left 折叠是继承迭代的,但您可以通过添加延续轻松地使它们递归。例如。

(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 没有帮助,因为参数顺序不遵循任何fold Scheme 中已知的实现。

递归调用在尾部位置,所以将它放在另一个过程调用中,如下所示:

(define (identity x) x)

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

现在递归调用不在尾部位置,不再是尾递归了。

如果编译器知道它什么都不做但希望它不会,则允许编译器优化身份函数。

与其做,不如写一个计划去做;只到最后,do:

(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遵循右折叠模式。它首先进行递归调用,然后才组成 returns 一个值,计划 first 将列表的头元素与 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。)