的代碼不起作用,因爲在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