帮助理解Scheme中的Continuations

时间:2010-01-07 03:22:03

标签: scheme lisp racket continuations the-little-schemer

我一直与 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)))

提前谢谢!!

2 个答案:

答案 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调用您的函数。现在,使用单元素列表,看看是什么它会调用你的函数。继续处理更长和更长的列表,直到你弄清楚发生了什么。

祝你好运!