枚举器#每个重新启动序列

时间:2013-05-05 18:12:22

标签: ruby sequence enumerator

我很惊讶Enumerator#每个都不会从序列中的当前位置开始。

o = Object.new

def o.each
  yield 1
  yield 2
  yield 3
end

e = o.to_enum
puts e.next
puts e.next
e.each{|x| puts x}
# I expect to see 1,2,3 but I see 1,2,1,2,3
# apparently Enumerator's each (inherited from Enumerable) restarts the sequence!

我做错了吗?有没有办法可以构建另一个具有预期的每个行为的枚举器(来自e)?

2 个答案:

答案 0 :(得分:2)

你没有做错,那不是为Enumerator#each定义的语义。您可以创建一个只从当前位置迭代到结尾的衍生枚举器:

class Enumerator
  def enum_the_rest
    Enumerator.new { |y| loop { y << self.next } }
  end
end

o = Object.new
def o.each
  yield 1
  yield 2
  yield 3
end

e = o.to_enum
=> #<Enumerator: ...>
e.next
=> 1
e2 = e.enum_the_rest
=> #<Enumerator: ...>
e2.each { |x| puts x }
=> 2
=> 3

而且,BTW,each不重启序列,它总是在整个范围内运行。您的枚举器仍然知道它与下一个next电话相关的位置。

e3 = o.to_enum
e3.next
=> 1
e3.next
=> 2
e3.map(&:to_s)
=> ["1", "2", "3"]
e3.next
=> 3

答案 1 :(得分:2)

Enumerator#nextEnumerator#each对该对象的处理方式不同。根据{{​​1}}(强调我的)的文档:

  

根据此Enumerable的构造方式迭代块。如果没有给出阻止,则返回self。

因此#each总是基于原始设置而不是当前内部状态。如果您在源头快速达到峰值,您将看到调用#each来设置新的枚举器。