Ruby如何实现Enumerator#next方法?

时间:2016-08-25 04:11:36

标签: ruby

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后   在

之内

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

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

1 个答案:

答案 0 :(得分:4)

  

我不明白第二个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