延续描述接下来会发生什么价值,对吧? 这不仅仅是一个获取值并进行一些计算的函数吗?
(+ (* 2 3) 5)
(* 2 3)
的延续是(+ _ 5)
(define k (lambda (v) (+ v 5)))
在此处使用call/cc
而不使用k
函数有什么意义?
答案 0 :(得分:6)
真。所有程序都有延续,直到它停止。一个延续通常是底层实现计算的一个步骤。
你的例子:
(+ (* 2 3) 5)
组合+取决于组合*首先完成。因此(+ result 5)
确实是(* 2 3)
的延续。但是,这不是这种情况下的程序。 call/cc
的用处是当你有一个遗憾而后悔并想要做其他事情或者你想在以后再回到这个时候。让我们做第一个:
(define g 0)
(call/cc
(lambda (exit)
(/ 10 (if (= g 0) (exit +Inf.0) g))))
显然,当if的结果完成时,有一个除法是继续,但是自exit
运行以来,整个事物被短路以返回+ Inf.0。
你怎么会用一个程序来做到这一点而不让它在之后进行划分?在这种风格中,你不能。
由于Scheme会将您的代码转换为Continuation Passing Style(=CPS)并且在CPS调用/ cc中没有特殊意义,因此这并不神奇。在CPS中编写代码并非易事。
以下是call/cc
(define (kcall/cc k consumer)
(consumer k (lambda (ignore v) (k v))))
答案 1 :(得分:4)
恭喜!你刚刚发明了延续传递风格!您已完成的工作与call/cc
之间的唯一区别是call/cc
会自动执行此操作,并且不需要您重新构建代码。
答案 2 :(得分:2)
'延续'是计算的整个未来。计算中的每个点都有一个延续,在幼稚的条件下,你可以将其视为当前的程序计数器和当前堆栈。 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值得注意的是,延续会让你回到顶级的read-eval-print循环 - 这使你有机会在这个例子中重新调用延续!