【问题标题】:callcc in ruby cause infinite loop?ruby中的callcc导致无限循环?
【发布时间】:2017-04-04 04:34:21
【问题描述】:

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

【问题讨论】:

  • “不起作用”是什么意思?你的标题提到了一个无限循环,你怎么看这个清单?
  • 是的,执行代码会无限输出“后期工作”。很抱歉有歧义。
  • entry.call(entry) 更改为 entry.call() 应该会给您所需的行为(尽管它仍然会因运行时错误而停止)
  • 别在意我之前的评论,它实际上并没有解决它。另一方面,Ruby 解释器确实发出警告,callcc 已被弃用,不应再使用
  • 是的,我试过只打印两次。还是谢谢

标签: ruby callcc


【解决方案1】:

代码不起作用,因为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 的 Ruby 实现在 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-27
    • 2021-05-07
    • 2012-08-24
    • 2020-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多