call/cc 是一种比生成器更通用的语言功能。因此,您可以使用call/cc 制作生成器,但您不能使用生成器制作call/cc。
如果您有一个计算值并在其他地方使用这些值的程序,它基本上是一台步进机器。人们可能会将其视为一个程序,每个步骤都有一个功能,其余步骤有一个延续。因此:
(+ (* 3 4) (* 5 6))
可以解释为:
((lambda (k)
(k* 3 4 (lambda (v34)
(k* 5 6 (lambda (v56)
(k+ v34 v56 k)))))
halt)
k 前缀只是表明它是原语的 CPS 版本。因此,他们将最后一个参数作为带有结果的函数调用。另请注意,在 Scheme 中未定义的评估顺序实际上是在此重写中选择的。用这种优美的语言call/cc 就是这样:
(define (kcall/cc kfn k)
(kfn (lambda (value ignored-continuation)
(k value))
k))
所以当你这样做时:
(+ (* 3 4) (call/cc (lambda (exit) (* 5 (exit 6)))))
; ==> 18
这发生在幕后:
((lambda (k)
(k* 3 4 (lambda (v34)
(kcall/cc (lambda (exit k)
(exit 6 (lambda (v6)
(k* 5 v6 k)))
k))))
halt)
通过使用替换,我们可以证明这实际上完全符合预期。由于调用了退出函数,因此永远不会调用原始延续,因此取消了计算。与call/cc 为我们提供这种似乎并不明显的延续相反,它在 CPS 中没有魔法。因此call/cc 的大部分魅力在于编译阶段。
(define (make-generator procedure)
(define last-return values)
(define last-value #f)
(define (last-continuation _)
(let ((result (procedure yield)))
(last-return result)))
(define (yield value)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda args
(call/cc (lambda (return)
(set! last-return return)
(if (null? args)
(last-continuation last-value)
(apply last-continuation args))))))
(define test
(make-generator
(lambda (collect)
(collect 1)
(collect 5)
(collect 10)
#f)))
(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)
可能make a macro to make the syntax more similar,但它只是在此之上的糖。
更多示例I love Matt Mights page 有很多关于如何使用延续的示例。