8

我不明白以下内容如何:

counts = Hash.new(0)

File.foreach("testfile") do |line|
  line.scan(/\w+/) do |word|
    word = word.downcase
    counts[word] += 1
  end
end

counts.keys.sort.each {|k| print "#{k}:#{counts[k]} "}

比:

words = Fiber.new do
  File.foreach("testfile") do |line|
    line.scan(/\w+/) do |word|
      Fiber.yield word.downcase
    end
  end
end

counts = Hash.new(0)

while word = words.resume
  counts[word] += 1
end

counts.keys.sort.each {|k| print "#{k}:#{counts[k]} "}
4

1 回答 1

27

Fiber 是一种暂停和恢复任意代码块的方法。像这样的例子并不是一个很好的用例,因为它与传统的按行读取和处理它们的方式相比并没有提供任何真正的优势。

在这个特定的例子中,如果你想让它变得更好,你会写一个枚举器风格的接口,这样你就可以写:

words = WordsReader.new("testfile")

words.each do |word|
  # ...
end

Fibers 变得重要的地方在于编写异步代码。例如,在EventMachine环境中,您需要能够发出异步调用、暂停代码块并在收到响应时恢复它。

这最终看起来像这样:

async_call(argument1, argument2) do |response_1|
  if (response_1.ok?)
    async_call(argument3, argument4) do |response_2|
      if (response_2.ok?)
        async_call(argument5, argument6) do |response_3|
          if (response_3.ok?)
            do_something(response_1, response_2, response_3)
          else
            panic_and_fail!
          end
        end
      else
        panic_and_fail!
      end
    end
  else
    panic_and_fail!
  end
end

这种嵌套、嵌套和重新嵌套的调用结构被松散地称为“回调地狱”,因为一旦你的逻辑变得不平凡,它就会变得非常难以管理。使这种结构变平的一种方法是使用纤维。一个适当的 Fiber-ized 等价物是:

begin
  response_1 = fiber_call(argument1, argument2)
  response_2 = fiber_call(argument3, argument4)
  response_3 = fiber_call(argument5, argument6)

  do_something(response_1, response_2, response_3)

rescue NotOkay
  panic_and_fail!
end

Fibers 可以利用异常,而回调类型的代码则不能。如您在此处所见,异常在有效使用时可以大大简化代码块。ok?预计调用将引发类型异常,而不是对每个响应进行测试NotOkay

回调不能可靠地抛出异常,因为回调发生时调用的发起者已经超出范围。这是使用回调进行异步编程的基本限制。Fiber 驱动的代码维护一个适当的调用堆栈,它只是按原样暂停和恢复,因此异常通过调用者适当地级联。

我发现 Fibers 既易于理解又很难正确应用。大多数情况下,您不必直接使用它们,而是使用使用它们的库。编写“光纤感知”代码与编写“线程安全”代码没有什么不同。做对可能很棘手。

于 2013-05-20T21:10:14.270 回答