我正在努力掌握延续的概念,我在维基百科的文章中找到了几个像这样的小教学例子:
(define the-continuation #f) (define (test) (let ((i 0)) ; call/cc calls its first function argument, passing ; a continuation variable representing this point in ; the program as the argument to that function. ; ; In this case, the function argument assigns that ; continuation to the variable the-continuation. ; (call/cc (lambda (k) (set! the-continuation k))) ; ; The next time the-continuation is called, we start here. (set! i (+ i 1)) i))
我理解这个小函数的作用,但是我看不出任何明显的应用.虽然我不希望很快在我的代码中使用延续,但我希望我知道一些它们可能适合的情况.
所以我正在寻找更多有用的代码示例,这些代码示例可以为程序员提供什么延续.
干杯!
在Algo和Data II中,我们一直使用这些来从(长)函数"退出"或"返回"
例如,用于遍历树的BFS算法实现如下:
(define (BFS graph root-discovered node-discovered edge-discovered edge-bumped . nodes) (define visited (make-vector (graph.order graph) #f)) (define q (queue.new)) (define exit ()) (define (BFS-tree node) (if (node-discovered node) (exit node)) (graph.map-edges graph node (lambda (node2) (cond ((not (vector-ref visited node2)) (when (edge-discovered node node2) (vector-set! visited node2 #t) (queue.enqueue! q node2))) (else (edge-bumped node node2))))) (if (not (queue.empty? q)) (BFS-tree (queue.serve! q)))) (call-with-current-continuation (lambda (my-future) (set! exit my-future) (cond ((null? nodes) (graph.map-nodes graph (lambda (node) (when (not (vector-ref visited node)) (vector-set! visited node #t) (root-discovered node) (BFS-tree node))))) (else (let loop-nodes ((node-list (car nodes))) (vector-set! visited (car node-list) #t) (root-discovered (car node-list)) (BFS-tree (car node-list)) (if (not (null? (cdr node-list))) (loop-nodes (cdr node-list)))))))))
如您所见,当节点发现的函数返回true时,算法将退出:
(if (node-discovered node) (exit node))
该函数还将给出"返回值":'node'
为什么函数退出,是因为这句话:
(call-with-current-continuation (lambda (my-future) (set! exit my-future)
当我们使用exit时,它将返回执行前的状态,清空调用堆栈并返回你给它的值.
基本上,call-cc用于(这里)跳出递归函数,而不是等待整个递归自行结束(在进行大量计算工作时这可能非常昂贵)
另一个与call-cc做同样的小例子:
(define (connected? g node1 node2) (define visited (make-vector (graph.order g) #f)) (define return ()) (define (connected-rec x y) (if (eq? x y) (return #t)) (vector-set! visited x #t) (graph.map-edges g x (lambda (t) (if (not (vector-ref visited t)) (connected-rec t y))))) (call-with-current-continuation (lambda (future) (set! return future) (connected-rec node1 node2) (return #f))))
海滨:
主页
维基百科页面
@拍
海滨
是的,Seaside就是一个很好的例子.我快速浏览了它的代码,发现这条消息说明了在Web上以一种看似有状态的方式在组件之间传递控制.
WAComponent >> call: aComponent "Pass control from the receiver to aComponent. The receiver will be temporarily replaced with aComponent. Code can return from here later on by sending #answer: to aComponent." ^ AnswerContinuation currentDo: [ :cc | self show: aComponent onAnswer: cc. WARenderNotification raiseSignal ]
很好!
我建立了自己的单元测试软件.在执行测试之前,我在执行测试之前存储延续,然后在失败时,我(可选)告诉方案解释器进入调试模式,并重新调用延续.这样我就可以轻松地逐步完成有问题的代码.
如果您的continuation是可序列化的,那么您还可以存储应用程序失败,然后重新调用它们以获取有关变量值,堆栈跟踪等的详细信息.
某些Web服务器和Web框架使用Continuations来存储会话信息.为每个会话创建一个continuation对象,然后由会话中的每个请求使用.
这里有一篇关于这种方法的文章.
我来到了防空火炮的实现,amb
运营商在这个职位从http://www.randomhacks.net,使用延续.
这是操作amb
员的作用:
# amb will (appear to) choose values # for x and y that prevent future # trouble. x = amb 1, 2, 3 y = amb 4, 5, 6 # Ooops! If x*y isn't 8, amb would # get angry. You wouldn't like # amb when it's angry. amb if x*y != 8 # Sure enough, x is 2 and y is 4. puts x, y
这是帖子的实现:
# A list of places we can "rewind" to # if we encounter amb with no # arguments. $backtrack_points = [] # Rewind to our most recent backtrack # point. def backtrack if $backtrack_points.empty? raise "Can't backtrack" else $backtrack_points.pop.call end end # Recursive implementation of the # amb operator. def amb *choices # Fail if we have no arguments. backtrack if choices.empty? callcc {|cc| # cc contains the "current # continuation". When called, # it will make the program # rewind to the end of this block. $backtrack_points.push cc # Return our first argument. return choices[0] } # We only get here if we backtrack # using the stored value of cc, # above. We call amb recursively # with the arguments we didn't use. amb *choices[1...choices.length] end # Backtracking beyond a call to cut # is strictly forbidden. def cut $backtrack_points = [] end
我喜欢amb
!