質問

I'm surprised that Enumerator#each doesn't start off at the current position in the sequence.

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!

Am I doin' it wrong? Is there a way to maybe construct another Enumerator (from e) that will have the expected each behavior?

役に立ちましたか?

解決

You're not doing it wrong, that's just not the semantics defined for Enumerator#each. You could make a derivative enumerator that only iterates from current position to end:

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

And, BTW, each doesn't restart the sequence, it just always runs over the entire span. Your enumerator still knows where it is in relation to the next next call.

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

他のヒント

Enumerator#next and Enumerator#each work on the object differently. Per the documentation for #each (emphasis mine):

Iterates over the block according to how this Enumerable was constructed. If no block is given, returns self.

So #each always behaves based on the original setup, not on the current internal state. If you quickly peak at the source you'll see that rb_obj_dup is called to setup a new enumerator.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top