的代码不起作用,因为在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
的代码更改回k.times
,则循环计数器再次卡住。这暴露了Ruby中延续对象的问题。
当继续倒退程序时,它可能回退或保留局部变量的值。我的程序假定entry.call
保留了循环计数器。 Matz的红宝石实现保留了Integer#my_times
中的循环计数器,但在Integer#times
中倒退循环计数器。这是我的程序无法使用Integer#times
的唯一原因。
MRI似乎在C代码中重新显示当地人(如Integer#times
),但保留Ruby代码中的本地人(如Integer#my_times
)。这使得循环计数器和其他当地人混乱。 Ruby不能解决这个问题,但是会对callcc
发出警告。 Ruby说,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)
你说的*“不工作”的意思是*?你的标题提到了一个无限循环,你怎么看到这个清单? – UnholySheep
是的,执行代码将无限地输出“以后的工作”。抱歉,模棱两可。 –
将'entry.call(entry)'改为'entry.call()'应该会给你想要的行为(虽然它仍然会因运行时错误而停止) – UnholySheep