尾递归理解

时间:2015-02-25 18:28:48

标签: recursion scheme racket

我想知道是否有人可以引导我通过尾递归。我在Racket中制作了这个程序,我想简单解释一下我应该采用什么步骤来利用尾部递归的形式。

Racket中的代码如下,

;SQUARE LIST FUNCTION
(define (square-list lst)
  (cond
    [(empty? lst) 0]
    [else (map (lambda (i)
                 (* i i)) lst)]))

或更好地定义为。

(define (square-list lst)
  (for-each (lambda (i)
    (printf "Iteration: ~a\n" (* i i)))lst))

所以我真的只想知道:

  • 如何将此过程更改为尾递归过程?

1 个答案:

答案 0 :(得分:0)

这两个示例都是tail call optimized,在这种意义上,解释器可以查看它,并且在第一种情况下看到cond始终作为此函数调用的最后一个表达式执行。这意味着它不会保持堆栈。在第二个示例中,它可以告诉for-each语句将始终最后执行。

出于教学目的,请看下面的函数:

(define (square-list ls)
    (if (empty? ls)
      '()
      (let ((first-square (* (car ls) (car ls))))
          (cons first-square (square-list (cdr ls)))))

在此功能中,您将计算第一个元素的平方。然后整个列表的平方的结果就是这个正方形,cons' d到函数的递归调用的前面,对列表的其余部分执行相同的操作。

关键在于,在这种情况下,解释器必须记住first-square,然后计算其余的正方形,最后创建一个列表。记忆部分是使函数非尾递归的原因。

因此,它基本上归结为通过构建输出并将中间结果连续调用传递给函数来帮助您的解释器尽可能少地记住。那我们怎么做呢?很简单,你将平方值传递给递归调用,并确保递归调用是函数体中的最后一个语句。

(define (square-list ls squares)
    (if (empty? ls)
      squares
      (let ((first-square (* (car ls) (car ls))))
          (square-list (cdr ls) (cons first-square squares)))

我们在这里做的是在函数调用中创建输出(正方形列表)。这意味着每个递归调用都会获得已经平方值的列表。因此,在身体中我们只是采用了#34;无标准"的价值。列出并将值加到平方值列表中。

因此调用此函数非常简单:

(square-list '(1 2 3) '())

在每次迭代中,我们将从输入列表中取出第一个值,将其平方并将其输出到输出列表。

(注意:这将产生反向平方列表)

REPL中的示例

Welcome to DrRacket, version 5.3.6 [3m].
Language: racket; memory limit: 128 MB.
> (define (square-list ls squares)
    (if (empty? ls)
      squares
      (let ((first-square (* (car ls) (car ls))))
          (square-list (cdr ls) (cons first-square squares)))))
> (square-list '(1 2 3) '())
'(9 4 1)
>