Recursion scheme-函数尾部是递归的吗?

Recursion scheme-函数尾部是递归的吗?,recursion,scheme,tail-recursion,Recursion,Scheme,Tail Recursion,我在scheme中实现了这个递归函数: (define f (lambda (n) (cond ((= n 0) 0) ((= n 1) 2) ((= n 2) 37) ((odd? n) (+ (f (- n 3)) 1)) (else (+ (f (- (/ n 2) 1)) 7)))) ) 在很多练习中,我被问到我的解决方案是不是

我在scheme中实现了这个递归函数:

       (define f (lambda (n)

        (cond ((= n 0) 0)
              ((= n 1) 2)
              ((= n 2) 37)
              ((odd? n) (+ (f (- n 3)) 1))
              (else (+ (f (- (/ n 2) 1)) 7))))
        )
在很多练习中,我被问到我的解决方案是不是尾部递归,老实说,不管我读了多少遍尾部递归的定义,我都不明白。如果我定义tailrecursive,它应该是这样的

定义:不管输入值是多少,尾函数都是在一个恒定的内存空间中计算的

这里还有另外两个。其中一个是尾部递归的,我想知道是哪一个

(define ln2-a
 (lambda (n)
 (define loop
   (lambda (n)
     (if (= n 1)
         0
        (+ 1 (loop (quotient n 2))))))
 (trace loop)
 (loop n)))
在这里

(define ln2-b
  (lambda (n)
    (define loop
      (lambda (n result)
      (if (= n 1)
         result
       (loop (quotient n 2) (+ result 1)))))
 (trace loop)
 (loop n 0)))
让我们看一下最后两个函数。这里我假设ln2-b是递归的。但我真的无法回答为什么,这让我有点恼火。我认为跟踪循环对我有帮助,但我不太确定它说了什么以及它如何帮助我。我试图比较这三个函数,找出它们的相似之处,但也找出它们之间的不同之处。但是,不幸的是,我无法做到这一点


希望友好的人能帮助我,谢谢。哦,我对计算机科学也很陌生,所以也许我用错了一些术语。如果有什么不清楚的地方,就说出来。:)

在第一个代码块中,f不是尾部递归的。必须执行对f的递归调用,返回其结果,然后根据分支将1或7添加到结果中。由于对f的递归调用必须返回,并且有任意多个对f的递归调用,这意味着必须分配任意多个堆栈帧

当你想到调用一个函数时,想象一下你拿了一张新的纸,把所有的局部变量和值都写在那张纸上可能会有所帮助。在确定函数的结果时,可能会对同一函数进行递归调用。如果调用是尾部调用,那么当你拿一张纸来调用时,你可以扔掉旧的表单,因为你不再需要它的任何值

例如,考虑两种计算列表长度的方法:

(define (length-1 list)
  (if (null? list)
      0
      (+ 1
         (length-1 (cdr list))))) ; recursive call, NOT a tail-call
在这个实现中,您必须完成对length-1的递归调用,获取其结果,然后向其中添加1。这意味着您需要在递归调用之后返回到某个地方。现在考虑一个尾部递归版本:

(define (length-2 list current-length)
  (if (null? list)
      current-length
      (length-2 (cdr list) ; recursive call, IS a tail-call
                (+ 1 current-length))))

在这个版本中,一旦开始递归调用length-2,就不再需要原始调用中的任何上下文。事实上,您可以通过将(cdr列表)分配给列表,并将(+1当前长度)分配给当前长度来将其转换为循环。可以重用相同的堆栈空间。这就是尾部调用优化(当尾部调用是对同一个函数时)等价于循环的方式。

在第一个代码块中,f不是尾部递归的。必须执行对f的递归调用,返回其结果,然后根据分支将1或7添加到结果中。由于对f的递归调用必须返回,并且有任意多个对f的递归调用,这意味着必须分配任意多个堆栈帧

当你想到调用一个函数时,想象一下你拿了一张新的纸,把所有的局部变量和值都写在那张纸上可能会有所帮助。在确定函数的结果时,可能会对同一函数进行递归调用。如果调用是尾部调用,那么当你拿一张纸来调用时,你可以扔掉旧的表单,因为你不再需要它的任何值

例如,考虑两种计算列表长度的方法:

(define (length-1 list)
  (if (null? list)
      0
      (+ 1
         (length-1 (cdr list))))) ; recursive call, NOT a tail-call
在这个实现中,您必须完成对length-1的递归调用,获取其结果,然后向其中添加1。这意味着您需要在递归调用之后返回到某个地方。现在考虑一个尾部递归版本:

(define (length-2 list current-length)
  (if (null? list)
      current-length
      (length-2 (cdr list) ; recursive call, IS a tail-call
                (+ 1 current-length))))

在这个版本中,一旦开始递归调用length-2,就不再需要原始调用中的任何上下文。事实上,您可以通过将(cdr列表)分配给列表,并将(+1当前长度)分配给当前长度来将其转换为循环。可以重用相同的堆栈空间。这就是尾部调用优化(当尾部调用是对同一个函数时)等价于循环的方式。

尾部递归函数将累积的结果传递到每个调用中,这样,一旦达到结束条件,该函数的最后一次调用就可以立即返回结果

非尾部递归函数将要求在返回结果后对其执行某些操作。因此,必须记住递归的每一层,以便它能够返回计算最终结果

在第一个示例中,您将添加到下一次调用f的结果中,因此它不是尾部递归的

第二个示例还添加到循环的下一个调用中,因此它不是尾部递归的


第三个示例将中的最终结果作为参数传递到下一个函数调用中,因此它是尾部递归的。

尾部递归函数将累积结果传递到每个调用中,这样一旦达到结束条件,该函数的最后一次调用就可以立即返回结果

非尾部递归函数将要求在返回结果后对其执行某些操作。因此,必须记住递归的每一层,以便它能够返回计算最终结果

在第一个示例中,您将添加到下一次调用f的结果中,因此它不是尾部递归的

第二个示例还添加到循环的下一个调用中,因此它不是尾部递归的

第三个示例将最终结果作为参数传递给下一个