在 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))))