该代码不起作用,因为循环计数器k.times
被卡住了。每次调用都会entry.call(entry)
将程序倒回到何时callcc
返回。所以callcc
再次返回,后面的工作再次发生,再次work
返回,然后k.times
重新开始。启动时k.times
,它将循环计数器重置为零。无限循环是因为循环计数器始终为零。
要修复程序,我们必须继续循环,而不是重新启动它。最好的解决方法是使用纤维,但首先,我尝试使用延续。这是适用于我的机器的版本:
require 'continuation'
def work
p "early work"
here = callcc {|here| here}
p "later work"
return here
end
class Integer
def my_times
i = 0
while i < self
yield i
i += 1
end
end
end
def rework(k)
entry = nil
k.my_times do |i|
if i == 0
entry = work
else
entry.call(entry)
end
end
end
rework(2)
work
我通过在循环内调用来修复控制流。再次work
返回时,我不会重置循环计数器。
我也定义了我自己的Integer#my_times
并且不使用 Ruby 的Integer#times
. 如果我将代码从k.my_times
back 更改为k.times
,循环计数器会再次卡住。这暴露了 Ruby 中延续对象的问题。
当延续倒退程序时,它可能会倒退或保留局部变量的值。我的程序假设entry.call
保留循环计数器。Matz 的 Ruby 实现在 中保留循环计数器Integer#my_times
,但在Integer#times
. 这是我的程序无法使用的唯一原因Integer#times
。
MRI 似乎在 C 代码(如Integer#times
)中倒带局部变量,但在 Ruby 代码(如Integer#my_times
)中保留局部变量。这会使循环计数器和其他本地人一团糟。Ruby 并没有解决这个问题,但警告不要使用callcc
. 红宝石说,warning: callcc is obsolete; use Fiber instead
。
这是使用光纤的程序:
def work
p "early work"
here = Fiber.new do
while true
p "later work"
Fiber.yield
end
end
here.resume
return here
end
def rework(k)
entry = nil
k.times do |i|
if i == 0
entry = work
else
entry.resume
end
end
end
rework(2)