Reputation: 739
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?
Upvotes: 3
Views: 208
Reputation: 6961
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.
Upvotes: 2
Reputation: 20408
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
Upvotes: 3