Ruby 寻找“的例子”;“真正的”;延续的使用
我试图理解延续性的概念,我从以下几个例子中找到了一些类似的小教学例子: 我理解这个小函数的作用,但我看不到它有任何明显的应用。虽然我不希望很快在我的代码中都使用continuations,但我希望知道一些合适的情况 因此,我正在寻找更明确有用的代码示例,以了解continuations可以为程序员提供什么Ruby 寻找“的例子”;“真正的”;延续的使用,ruby,language-agnostic,scheme,smalltalk,continuations,Ruby,Language Agnostic,Scheme,Smalltalk,Continuations,我试图理解延续性的概念,我从以下几个例子中找到了一些类似的小教学例子: 我理解这个小函数的作用,但我看不到它有任何明显的应用。虽然我不希望很快在我的代码中都使用continuations,但我希望知道一些合适的情况 因此,我正在寻找更明确有用的代码示例,以了解continuations可以为程序员提供什么 干杯 只要程序流不是线性的,甚至不是预先确定的,就可以在“现实生活”示例中使用延续。常见的情况是。一些web服务器和web框架使用Continuations来存储会话信息。为每个会话创建一个c
干杯 只要程序流不是线性的,甚至不是预先确定的,就可以在“现实生活”示例中使用延续。常见的情况是。一些web服务器和web框架使用Continuations来存储会话信息。为每个会话创建一个continuation对象,然后由会话中的每个请求使用 海边:
- @Pat
海边
是的,这是一个很好的例子。我快速浏览了它的代码,发现了这样一条信息,说明了在组件之间以一种似乎全状态的方式通过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 ]
太好了 在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))
该函数还将给出一个“返回值”:“节点”
函数退出的原因在于以下语句:
(call-with-current-continuation
(lambda (my-future)
(set! exit my-future)
当我们使用exit时,它将返回到执行前的状态,清空调用堆栈并返回您给它的值
因此,基本上,调用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))))
我在from中使用continuations实现了
amb
操作符
以下是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 我构建了自己的单元测试软件。在执行测试之前,我在执行测试之前存储延续,然后在失败时,我(可选)告诉scheme解释器进入调试模式,并重新调用延续。通过这种方式,我可以很容易地逐步完成有问题的代码
如果您的continuations是可序列化的,那么您还可以在应用程序失败时存储它们,然后重新调用它们以获取有关变量值、堆栈跟踪等的详细信息。continuations是服务器编程(包括web应用程序前端)中每个请求线程的一个很好的替代方案 在这个模型中,不是每次请求进来时都启动一个新的(沉重的)线程,而是在函数中开始一些工作,您将一个延续传递到网络响应处理程序。当响应返回时,您执行延续。使用此方案,您只需几个线程即可处理大量请求 这使得控制流比使用阻塞线程更复杂,但在高负载下,它更高效(至少在今天的硬件上)。如何?有一系列函数(都以
异步
结尾)API函数执行一个异步请求,获取结果,然后将结果传递给回调函数(作为“下一步要做的事情”)。听起来很像
这是一个非常简单的例子
map.getZoomAsync(function(zoom) {
alert("Current zoom level is " + zoom); // this is the continuation
});
alert("This might happen before or after you see the zoom level message");
由于这是Javascript,因此堆栈将随着每次调用而增长,成为一个延续,最终您将把控制线程返回到浏览器。尽管如此,我认为这是一个很好的抽象。延续可以用于实现异常,一个调试器。如果您必须调用异步操作,并挂起exe在得到结果之前,您通常会轮询结果或将其余代码放入回调中,在完成时由异步操作执行。使用continuations,您不需要执行效率低下的轮询选项,也不需要将所有代码打包,以便在回调中的异步事件后运行-您只需将代码的当前状态作为回调进行传递,一旦异步操作完成,代码就会被有效地“唤醒”。amb操作符就是一个很好的示例,它允许类似prolog的声明性编程 正如我们所说,我正在用Scheme编写一个音乐创作软件(我是一个几乎不懂音乐背后的理论的音乐家,我只是在分析我自己的作品,看看它背后的数学原理是如何运作的。) 使用amb操作符,我可以只填充旋律必须满足的约束,并让Scheme计算出结果
Continuations可能被放入Scheme是因为语言哲学,Scheme是一个框架,通过在Scheme中定义库,您可以了解其他语言中的任何编程范式。Continuations用于构建您自己的抽象控制结构,如“return”、“break”或启用声明式编程Scheme更具“通用性”,并要求程序员也能指定此类结构。是的,Seaside是一个很好的例子,我在一个6个月的项目中使用了它!不过我正在寻找代码示例。
map.getZoomAsync(function(zoom) {
alert("Current zoom level is " + zoom); // this is the continuation
});
alert("This might happen before or after you see the zoom level message");