最初我发布了一个问题" understanding a tail-recursive vector->list answer"这是其他问题。我对计划的一般理解真的很模糊。所以我现在还有几个问题:
;;;;;; original code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define vector->list:rec
(lambda (v)
(letrec ((helper
(lambda (vec r i)
(if (< i 0)
r
(helper vec (cons (vector-ref v i) r) (- i 1)) ;; Q1
))))
(if (> (vector-length v) 0) ;; line 9
(helper v ;; line 10
(cons (vector-ref v (- (vector-length v) 1)) '())
(- (vector-length v) 2))
'()))))
Q2)尾递归让我很难理解。我理解为什么他们需要尾递归,基本上他们使用它来避免迭代,所以他们使用helper作为中间例程..所以他们可以避免把每个迭代放入堆栈....就像这样。和letrec / lambda表达式一样:
;;;;;;;;;letrec/lambda express;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (some-procedure...)
(letrec ((helper (lambda (x)
...
(if some-test?
(helper ...))))) ; recursive call --- Q2-1
...
(helper ...) ; call to recursive local procedure ---- Q2-2
...))
第Q2-2行:为什么这是本地递归&#39;? &#39;地方&#39;听起来对中间例程递归...这里中间意味着我的理解..
[我的困惑]尾递归不应该迭代(调用)本身直到整个程序结束 - 所以不应该在中间例程中。= shouldn&#39在帮助者里面?根据我的理解,直到现在......帮助器是用于封装在letrec表达式中的中间例程。?。)所以最后只调用自己。(我的意思是:在letrec ...之外?)。
答案 0 :(得分:2)
首先,我会稍微改写一下你的例子:
(define (vector->list-iter v)
(let loop ((i (- (vector-length v) 1)) (acc '()))
(if (< i 0)
acc
(loop (- i 1) (cons (vector-ref v i) acc)))))
要看到差异,我们可以制作非尾递归版本:
(define (vector->list-rec v)
(define len (vector-length v))
(let loop ((i 0))
(if (>= i len)
'()
(cons (vector-ref v i) (loop (+ i 1))))))
Scheme中没有循环功能。它只是不会增加堆栈的递归和递归,因为在上一步中还有更多的事情要做,称为尾递归。
由于我们可以以任何方式迭代向量(它的O(1)访问),我们可以将它迭代到第一个或从头到尾迭代。由于列表只能在最后一次进行,因此非尾递归版本不会在第一个元素上应用cons
,直到它创建了除第一个元素之外的整个列表。这使得5元素向量在击中基本情况时具有5个连续。如果它是一个大向量,它可能导致堆栈溢出。
第一个例子首先使列表包含最后一个元素,并在它完成时进行递归。它不需要包含任何东西,因为在递归之前完成了收益。并非所有可以像这样处理的问题。想象一下,你想要复制一个列表。它可以从头到尾迭代,但从头到尾构建。没有突变或额外的消耗,没有办法使这样的程序尾递归。