【问题标题】:How Ruby implements Enumerator#next method?Ruby 如何实现 Enumerator#next 方法?
【发布时间】:2016-08-25 04:11:36
【问题描述】:
class MyString
  include Enumerable
  def initialize(n)
    @num = n
  end
  def each
    i = 0
    while i < @num
      yield "#{i} within while"
      puts "After yield #{i}"
      i += 1
    end
  end
end

s = MyString.new(10)
a = s.to_enum
puts "first"
puts a.next
puts "second"
puts  a.next

我的 ruby​​ 版本是 2.2.5,代码输出是

第一
0 以内
第二
产后0
1 以内

我认为执行流程是first a.next-&gt;s.each-&gt;while-&gt;yield-&gt;second a.next-&gt;jump into while loop 我的问题是 Enumerator#next 方法是如何实现的?

我可能知道调用的块 yield 中有中断,这导致 yield-&gt;second a.next;但是,我不明白第二个 a.next 是如何跳回 while 循环的。

【问题讨论】:

  • 我猜让你感到困惑的是yield关键字,而不是next是如何实现的。
  • @halfelf:我相信这与Enumerator 有很大关系:在引入光纤之前,这在 1.8 中是不可能的。 Plain yield 仅适用于块,并且 OP 的代码中没有块。这意味着捕获tield 的块在Enumerator 内部。

标签: ruby


【解决方案1】:

我不明白第二个 a.next 是如何跳回 while 循环的。

魔法。Enumerator(和Fiber)的超能力。

这两个类是在 Ruby 1.9 中引入的,并且有很多相似之处;特别是,它们允许您进行手动协作绿色线程。

让我们先看看纤维,因为它们更基本:

f = Fiber.new do
  puts "A"
  Fiber.yield 1
  puts "B"
  Fiber.yield 2
  puts "C"
end

puts "First"    # First
puts f.resume   # A
                # 1
puts "Second"   # Second
puts f.resume   # B
                # 2
puts "End"      # End
f.resume        # C
f.resume        # FiberError: dead fiber called

基本上,纤程就像一个线程,但它会在产生Fiber.yield 时暂停,并在被Fiber#resume 恢复时恢复。它是在 C 中作为 Ruby 的基本功能实现的,因此作为 Ruby 的学生(与 Ruby interpreter 的学生相反),您不需要知道它是如何工作的, 就是这样(就像你需要知道IO#read 会读取一个文件,但不一定知道它是如何在 C 中实现的)。

Enumerator 几乎是相同的概念,但适用于迭代(而Fiber 更多用途)。事实上,我们可以用Enumerator 几乎一字不差地写出上面的内容:

e = Enumerator.new do |yielder|
  puts "A"
  yielder.yield 1
  puts "B"
  yielder.yield 2
  puts "C"
end

puts "First"    # First
puts e.next     # A
                # 1
puts "Second"   # Second
puts e.next     # B
                # 2
puts "End"      # End
e.next          # C
                # StopIteration: iteration reached an end

【讨论】:

  • “基本上,纤维就像一个线程”——我会说它更像是一个半协程而不是线程。
  • @JörgWMittag:AFAIK,纤维不是半协程,它们半协程。但是,很多人都知道线程,而半协程并不广为人知。
猜你喜欢
  • 2013-10-06
  • 2011-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 2012-01-23
相关资源
最近更新 更多