Continuation用一些值描述接下来会发生什么,对吧?这不就是一个取一个值并进行一些计算的函数吗?
(+ (* 2 3) 5)
(* 2 3)
的延续是(+ _ 5)
(define k (lambda (v) (+ v 5)))
在这里使用call/cc
而不使用函数k
有什么意义?
True。所有程序都有延续,直到它停止。一个延续通常是底层实现进行计算的一个步骤。
你的例子:
(+ (* 2 3) 5)
组合+取决于组合*以获得第一名。因此CCD_ 5实际上是CCD_ 6的延续。不过,在这种情况下,这不是一个过程。call/cc
的有用之处在于,当你有一个延续时,你会后悔并想做其他事情,或者你想在以后再回来。让我们做第一个:
(define g 0)
(call/cc
(lambda (exit)
(/ 10 (if (= g 0) (exit +Inf.0) g))))
显然,当if的结果完成时,有一个除法是连续的,但由于运行了exit
,整个过程短路,返回+Inf.0。
如果不让它在事后进行除法运算,你怎么能用一个程序做到这一点?在这种风格下,你不能。
这其实并不神奇,因为Scheme将您的代码转换为ContinuationPassingStyle(=CPS),并且在CPS中调用/cc也没有什么特别之处。在CPS中编写代码并非易事。
以下是call/cc
的CPS定义
(define (kcall/cc k consumer)
(consumer k (lambda (ignore v) (k v))))
祝贺您!你刚刚发明了连续传球风格!您所做的与call/cc
的唯一区别在于,call/cc
是自动完成的,不需要重新构造代码。
"continuation"是计算的全部未来。计算中的每个点都有一个延续,简单地说,你可以把它看作是当前程序计数器和当前堆栈。Scheme call/cc
函数方便地捕获当前配置并将其打包为一个函数。当您调用该函数时,您将返回到计算中的那个点。因此,连续性与函数非常不同(但连续性函数是函数)。
通常有两种常见的情况可以看到call/cc
的应用:
-
非本地出口。建立一个连续性,进行一些计算,以突然结束调用该连续性的计算。
-
重新启动/重新输入计算。在这种情况下,您保存延续,然后根据需要再次调用它。
以下是案例#1的示例:
(begin
;; do stuff
(call/cc (lambda (k)
;; do more
;; oops, must 'abort'
(k 'ignore)))
;; continue on
)
下面是案例#2的一个例子:
> (define c #f)
> (let ((x 10))
(display (list (+ 1 (call/cc (lambda (k) (set! c k) x))) 111))
(display " more"))
(11 111) more
> (c 20)
(21 111) more
> (c 90)
(91 111) more
对于第2种情况,值得注意的是,continuation会将您带回顶级的read-eval-print循环,这使您有机会在本例中重新调用continuation!