3

我正在尝试复习课堂幻灯片。该代码应该打印一次“早期工作”,然后打印两次“后期工作”(您可以设置后期工作的重复次数)。但我想知道为什么这段代码不起作用,我该如何修改代码?由于现在代码将生成“稍后工作”的无限循环,而不是 2(应该是)

require 'continuation'
def work
  p "early work"
  here = callcc {|here| here}
  p "later work"
  return here
end

def rework(k)
  entry = work
  k.times do |i|
    entry.call(entry)
  end
end

rework(2)
4

1 回答 1

2

该代码不起作用,因为循环计数器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_timesback 更改为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)
于 2017-10-05T03:22:34.450 回答