我一直与 The Little Schemer 一起学习Scheme并在我的环境中使用PLT-Scheme。
Little Schemer 对递归给了我很大的帮助(现在对我来说很简单)但是我仍然坚持介绍“收藏家”这本书的一部分并将整个功能称为“收藏家”延续。
以下是他们使用的示例代码。我理解递归元素但是我被卡住了,特别是在lambda函数上 - 我的思想不能遵循路径以及如何设置lambda函数的参数(因为他们唯一的调用是在递归中再次调用它们,有在功能体内没有具体用途。)
如果有人能够通过将函数递归到lambda收集器中或多或少地让我分解计算路径,那可能对我有帮助。
;; Build a nested list of even numbers by removing the odd ones from its
;; argument and simultaneously multiply the even numbers and sum the odd
;; numbers that occur in its argument.
(define (even-only-collector l col)
(cond
((null? l)
(col (quote ()) 1 0))
((atom? (car l))
(cond
((even? (car l))
(even-only-collector (cdr l)
(lambda (newl p s)
(col (cons (car l) newl)
(* (car l) p) s))))
(else
(even-only-collector (cdr l)
(lambda (newl p s)
(col newl
p (+ (car l) s)))))))
(else
(even-only-collector (car l)
(lambda (al ap as)
(even-only-collector (cdr l)
(lambda (dl dp ds)
(col (cons al dl)
(* ap dp)
(+ as ds)))))))))
;; The collector function
(define (collector newl product sum)
(cons sum
(cons product newl)))
提前谢谢!!
答案 0 :(得分:43)
尝试更简单的方法来了解其工作原理。例如,这是一个list-sum
函数的版本,它接收一个continuation参数(通常称为k
):
(define (list-sum l k)
(if (null? l)
???
(list-sum (cdr l) ???)))
基本模式就在那里,缺失的部分是有趣的事情发生的地方。 continuation参数是一个期望接收结果的函数 - 所以如果列表为null,很明显我们应该发送它0
,因为这是总和:
(define (list-sum l k)
(if (null? l)
(k 0)
(list-sum (cdr l) ???)))
现在,当列表不为null时,我们用列表的尾部递归调用函数(换句话说,这是一个迭代),但问题是延续应该是什么。这样做:
(define (list-sum l k)
(if (null? l)
(k 0)
(list-sum (cdr l) k)))
显然是错误的 - 这意味着k
最终将获得(cdr l)
的总和,而不是l
的所有内容。相反,在那里使用一个新函数,它将总结l
的第一个元素以及它收到的值:
(define (list-sum l k)
(if (null? l)
(k 0)
(list-sum (cdr l) (lambda (sum) (+ (car l) sum)))))
这越来越近,但仍然是错误的。但是考虑事情的运作方式是一个很好的观点 - 我们正在调用list-sum
一个本身会收到总和的延续,并添加我们现在看到的第一个项目。我们忽略k
这一事实显而易见。我们需要的是使用此函数撰写 k
- 所以我们执行相同的求和操作,然后将结果发送到k
:
(define (list-sum l k)
(if (null? l)
(k 0)
(list-sum (cdr l) (compose k (lambda (s) (+ s (car l)))))))
终于有效了。 (顺便说一句,请记住,这些lambda
函数中的每一个都有自己的l
副本。)您可以尝试使用:
(list-sum '(1 2 3 4) (lambda (x) x))
最后请注意,这与:
相同(define (list-sum l k)
(if (null? l)
(k 0)
(list-sum (cdr l) (lambda (s) (k (+ s (car l)))))))
如果你明确表达了作品。
(您也可以在中间+ lambda学生语言中使用此代码,然后单击步进按钮以查看评估是如何进行的 - 这将需要一段时间才能完成,但您将看到延续函数如何获得嵌套,每个都有自己的列表视图。)
答案 1 :(得分:1)
这是帮助您“获得更具体想法”的一种方法。想象一下,如果收集器是这样定义的:
(define (collector l p s)
(display l)
(newline)
(display p)
(newline)
(display s)
(newline))
您可以在基本情况下看到,如果传入一个空列表,它将使用参数'()
,1和0调用您的函数。现在,使用单元素列表,看看是什么它会调用你的函数。继续处理更长和更长的列表,直到你弄清楚发生了什么。