Lisp 这个函数是递归的吗?

Lisp 这个函数是递归的吗?,lisp,scheme,racket,tail-recursion,Lisp,Scheme,Racket,Tail Recursion,在racket中,我定义了以下函数,我想知道它是否是尾部递归的: 我的问题实际上是这样的,但我必须写些别的东西来满足我的帖子质量标准。实际上,我不知道我的帖子质量标准是什么。对foo的唯一调用是在尾部位置,因此该函数在我看来是尾部递归的。对foo的唯一调用是在尾部位置,因此该函数在我看来是尾部递归的。这实际上与你之前的问题相同。是的,这是尾部递归:每当在函数foo中发生递归调用时,它都处于尾部位置。意思是:执行递归调用后,无需执行其他操作,该执行分支结束。cons s1 s2部分是递归的基本情况

在racket中,我定义了以下函数,我想知道它是否是尾部递归的:


我的问题实际上是这样的,但我必须写些别的东西来满足我的帖子质量标准。实际上,我不知道我的帖子质量标准是什么。

对foo的唯一调用是在尾部位置,因此该函数在我看来是尾部递归的。

对foo的唯一调用是在尾部位置,因此该函数在我看来是尾部递归的。

这实际上与你之前的问题相同。是的,这是尾部递归:每当在函数foo中发生递归调用时,它都处于尾部位置。意思是:执行递归调用后,无需执行其他操作,该执行分支结束。cons s1 s2部分是递归的基本情况,所以不算数。为了更清楚地了解这一点,foo程序相当于:

(define (foo c m s1 s2)
  (cond ((>= c m)
         (cons s1 s2))                  ; base case of recursion
        ((= (modulo m c) 0)
         (foo (+ c 1) m (+ s1 c) s2))   ; recursive call is in tail position
        (else
         (foo (+ c 2) m s1 (+ s2 c))))) ; recursive call is in tail position
让我们看一个不是尾部递归的例子。例如,如果第二个if的后续部分定义如下:

(+ 1 (foo (+ c 1) m (+ s1 c) s2))

显然,递归调用不会处于尾部位置,因为在递归返回后,将执行一个操作:将一个操作添加到递归结果中。

这实际上与前面的问题相同。是的,这是尾部递归:每当在函数foo中发生递归调用时,它都处于尾部位置。意思是:执行递归调用后,无需执行其他操作,该执行分支结束。cons s1 s2部分是递归的基本情况,所以不算数。为了更清楚地了解这一点,foo程序相当于:

(define (foo c m s1 s2)
  (cond ((>= c m)
         (cons s1 s2))                  ; base case of recursion
        ((= (modulo m c) 0)
         (foo (+ c 1) m (+ s1 c) s2))   ; recursive call is in tail position
        (else
         (foo (+ c 2) m s1 (+ s2 c))))) ; recursive call is in tail position
让我们看一个不是尾部递归的例子。例如,如果第二个if的后续部分定义如下:

(+ 1 (foo (+ c 1) m (+ s1 c) s2))
显然,递归调用不会处于尾部位置,因为在递归返回后,将执行一个操作:将一个操作添加到递归结果中。

的第59页第11.20节描述了尾部调用,并显示了基本模式语法形式的尾部调用位置,如if和lambda

您在foo中对foo的调用处于尾部位置。因为它们位于内部if尾部位置、外部if尾部位置和lambda尾部位置。

的第59页第11.20节描述了尾部调用,并显示了基本方案语法形式的尾部调用位置,如if和lambda


您在foo中对foo的调用处于尾部位置。因为它们位于内部if尾部位置、外部if尾部位置和lambda尾部位置。

这里有一个伪代码公共Lisp,实际上是代码到帧突变版本的转换:

(defun foo (c m s1 s2)
  (prog 
      ((c c) (m m) (s1 s1) (s2 s2))  ; the frame
      BACK
      (if (< c m)
          (if (= (modulo m c) 0)
              (progn 
                (psetf s1 (+ s1 c)     ; set!
                       c  (+ c  1))    ;   in parallel
                (go BACK))
              (progn 
                (psetf s2 (+ s2 c)     ; set!
                       c  (+ c  2))    ;   in parallel
                (go BACK)))
          (return-from foo (cons s1 s2))))))

由于每次尾部调用后都没有什么事情可做,我们可以返回。

这里有一个伪代码公共Lisp,实际上是将代码转换为帧突变版本:

(defun foo (c m s1 s2)
  (prog 
      ((c c) (m m) (s1 s1) (s2 s2))  ; the frame
      BACK
      (if (< c m)
          (if (= (modulo m c) 0)
              (progn 
                (psetf s1 (+ s1 c)     ; set!
                       c  (+ c  1))    ;   in parallel
                (go BACK))
              (progn 
                (psetf s2 (+ s2 c)     ; set!
                       c  (+ c  2))    ;   in parallel
                (go BACK)))
          (return-from foo (cons s1 s2))))))

由于在每次尾部调用之后都没有更多的事情要做,所以我们可以返回。

我很难想象解释器如何重复使用与本例中优化相同的调用堆栈。在我上面的实现中更清楚,将foo的尾部调用看作是一种奇怪的查找,其中参数是在每次迭代中更新的变量,第一个条件是循环的退出条件。然后很容易看到调用之间重复使用了相同的堆栈帧-仅需要更新参数值对我来说很难想象解释器如何重复使用与本例优化相同的调用堆栈。在我上面的实现中更清楚,将foo的尾部调用看作是一种奇怪的查找,其中参数是在每次迭代中更新的变量,第一个条件是循环的退出条件。然后很容易看到相同的堆栈框架在调用之间被重用——只需要更新参数值