给定方案中的递归函数如何将该函数更改为tail递归,然后如何使用流实现它?在以这种方式更改任何功能时,您是否遵循了模式和规则?
以此函数为例,创建一个2米的数字列表(这不是尾递归?)
代码:
(define listupto
(lambda (m)
(if (= m 2)
'(2)
(append (listupto (- m 1)) (list m)))))
答案 0 :(得分:4)
我将从解释你的例子开始。绝对不是尾递归。想想这个函数是如何执行的。每次你追加时,你必须先回去做一个递归的电话,直到你碰到基本情况,然后再拉回来。
这就是你的功能痕迹:
(listupto 4)
| (append (listupto(3)) '4)
|| (append (append (listupto(2)) '(3)) '(4))
||| (append (append '(2) '(3)) '(4))
|| (append '(2 3) '(4))
| '(2 3 4)
'(2 3 4)
请注意您看到的V-pattern拉入然后退出递归调用。尾递归的目标是将所有调用一起构建,并且只进行一次执行。你需要做的是传递一个累加器和你的函数,这样你只能在你的函数到达基本情况时做一个追加。
这是函数的尾递归版本:
(define listupto-tail
(lambda (m)
(listupto m '())))
# Now with the new accumulator parameter!
(define listupto
(lambda (m accu)
(if (= m 2)
(append '(2) accu)
(listupto (- m 1) (append (list m) accu)))))
如果我们看到此痕迹,它将如下所示:
(listupto 4)
| (listupto (3) '(4)) # m appended with the accu, which is the empty list currently
|| (listupto (2) '(3 4)) # m appended with accu, which is now a list with 4
||| (append '(2) '(3 4))
'(2 3 4)
注意模式是如何不同的,我们不必遍历递归调用。这为我们节省了无意义的执行。尾递归可能是一个难以掌握的概念我建议看看here。第5章有一些有用的部分。
答案 1 :(得分:3)
通常要切换到尾递归形式,您需要转换代码,以便它采用累加器参数来构建结果,并用作最终返回值。这通常是一个辅助函数,你的主函数也代表它。
某种形式:
(define listupto
(lambda (m)
(listupto-helper m '())))
(define listupto-helper
(lambda (m l)
(if (= m 2)
(append '(2) l)
(listupto-helper (- m 1) (append (list m) l)))))
正如评论所指出的那样,帮助函数可以用一个名为let的替换,这显然(没有做太多/足够的Scheme!)更具惯用性(并且评论建议cons
要好得多。创建一个列表并附加。
(define listupto
(lambda (n)
(let loop ((m n) (l '()))
(if (= m 2)
(append '(2) l)
(loop (- m 1) (cons m l))))))
答案 2 :(得分:0)
你也问过溪流。您可以找到使用的SICP样式的流,例如here已定义from-By
流构建器:
;;;; Stream Implementation
(define (head s) (car s))
(define (tail s) ((cdr s)))
(define-syntax s-cons
(syntax-rules () ((s-cons h t) (cons h (lambda () t)))))
;;;; Stream Utility Functions
(define (from-By x s)
(s-cons x (from-By (+ x s) s)))
此类流创建依赖于宏,必须通过特殊方式访问它们:
(define (take n s)
(cond ((> n 0) (cons (head s) (take (- n 1) (tail s))))
(else ())))
(define (drop n s)
(cond ((> n 0) (drop (- n 1) (tail s)))
(else s)))
但它们不是持久性的,即take
和drop
在每次访问时重新计算它们。要制作持久性流,您需要安排尾部闭合,以便在访问时手术改变最后一对:
(1 . <closure-1>)
(1 . (2 . <closure-2>))
....
像这样:
(define (make-stream next this init)
(let ((c (list (this init))))
(letrec ((g (lambda ()
(set! init (next init))
(set-cdr! c (cons (this init) g))
(set! c (cdr c))
c)))
(set-cdr! c g)
c)))
(define (head s) (car s))
(define (tail s)
(if (pair? (cdr s)) (cdr s)
(if (not (null? (cdr s)))
((cdr s)))))
我们现在可以像这样使用它
(define a (make-stream (lambda(i) (+ i 1)) (lambda(i) i) 1))
;Value: a
a
;Value 13: (1 . #[compound-procedure 14])
(take 3 a)
;Value 15: (1 2 3)
a
;Value 13: (1 2 3 4 . #[compound-procedure 14])
(define b (drop 4 a))
;Value: b
b
;Value 16: (5 . #[compound-procedure 14])
a
;Value 13: (1 2 3 4 5 . #[compound-procedure 14])
(take 4 a)
;Value 17: (1 2 3 4)
a
;Value 13: (1 2 3 4 5 . #[compound-procedure 14])
现在,(make-stream (lambda(i)(list (cadr i) (+ (car i) (cadr i)))) car (list 0 1))
定义了什么?
答案 3 :(得分:0)
这是尾递归形式-
(define (listupto n)
(let run
((m 0)
(return identity))
(if (> m n)
(return null)
(run (add1 m)
(lambda (r) (return (cons m r)))))))
(listupto 9)
; '(0 1 2 3 4 5 6 7 8 9)
这里是流-
(define (listupto n)
(let run
((m 0))
(if (> m n)
empty-stream
(stream-cons m
(run (add1 m))))))
(stream->list (listupto 9))
; '(0 1 2 3 4 5 6 7 8 9)