Recursion 尾部递归理解
我想知道是否有人能带我经历尾部递归。我在Racket中做了这个过程,我想简单解释一下我应该采取什么步骤来利用尾部递归的形式 球拍中的代码如下:Recursion 尾部递归理解,recursion,scheme,racket,Recursion,Scheme,Racket,我想知道是否有人能带我经历尾部递归。我在Racket中做了这个过程,我想简单解释一下我应该采取什么步骤来利用尾部递归的形式 球拍中的代码如下: ;SQUARE LIST FUNCTION (define (square-list lst) (cond [(empty? lst) 0] [else (map (lambda (i) (* i i)) lst)])) 或者更好地定义为 (define (square-list lst) (f
;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))
所以我真的只想知道:
- 我应该如何将此过程更改为尾部递归过程
尾部调用优化的
,从这个意义上讲,解释器可以看到,在第一种情况下,cond
始终作为此函数调用的最后一个表达式执行。这意味着它不保持堆栈。在第二个示例中,它可以看出每个语句的总是最后执行
出于教学目的,请查看以下功能:
(define (square-list ls)
(if (empty? ls)
'()
(let ((first-square (* (car ls) (car ls))))
(cons first-square (square-list (cdr ls)))))
在此函数中,您将计算第一个元素的平方。然后,整个列表的平方的结果就是这个平方,cons
'd到递归调用函数的前面,以对列表的其余部分执行相同的操作
关键是,在这种情况下,解释器必须记住第一个方块
值,然后计算剩余的方块,最后从中创建一个列表。记忆部分是使函数非尾部递归的原因
因此,它基本上可以通过建立输出并将中间结果连续调用传递给函数来帮助解释器尽可能少地记住。那我们怎么做呢?简单地说,将平方值传递到递归调用中,并确保递归调用是函数体中的最后一条语句
(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) '())
在每次迭代中,我们将从输入列表中获取第一个值,将其平方,并将其转换为输出列表
(注:这将产生反向平方列表)
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)
>
首先做一个递归过程。然后从这里开始。缺少递归函数意味着他/她并不真正理解它是如何工作的,所以我也继续解释了。平方值在第二个平方列表中起什么作用?无论放在那里的是什么,它似乎都是一个虚线列表。末尾的cons表示每个方块都被添加到方块中,对吗?为什么我必须有一个平方的值?正如我在回答中所解释的,你需要把计算出的值(即平方)保存在函数的一个参数中。如果没有,你必须在你的函数体中记住它们。如果我执行上面的函数,我会得到一个正确的列表(顺序相反)。