A class which allows both internal and external iteration.
An Enumerator can be created by the following methods.
{Kernel#to_enum}
{Kernel#enum_for}
{Enumerator#initialize ::new}
Most methods have two forms: a block form where the contents are evaluated for each item in the enumeration, and a non-block form which returns a new Enumerator wrapping the iteration.
enumerator = %w(one two three).each puts enumerator.class # => Enumerator enumerator.each_with_object("foo") do |item, obj| puts "#{obj}: #{item}" end # foo: one # foo: two # foo: three enum_with_obj = enumerator.each_with_object("foo") puts enum_with_obj.class # => Enumerator enum_with_obj.each do |item, obj| puts "#{obj}: #{item}" end # foo: one # foo: two # foo: three
This allows you to chain Enumerators together. For example, you can map a list's elements to strings containing the index and the element as a string via:
puts %w[foo bar baz].map.with_index { |w, i| "#{i}:#{w}" } # => ["0:foo", "1:bar", "2:baz"]
An Enumerator can also be used as an external iterator. For example, #next returns the next value of the iterator or raises StopIteration if the Enumerator is at the end.
e = [1,2,3].each # returns an enumerator object. puts e.next # => 1 puts e.next # => 2 puts e.next # => 3 puts e.next # raises StopIteration
You can use this to implement an internal iterator as follows:
def ext_each(e) while true begin vs = e.next_values rescue StopIteration return $!.result end y = yield(*vs) e.feed y end end o = Object.new def o.each puts yield puts yield(1) puts yield(1, 2) 3 end # use o.each as an internal iterator directly. puts o.each {|*x| puts x; [:b, *x] } # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3 # convert o.each to an external iterator for # implementing an internal iterator. puts ext_each(o.to_enum) {|*x| puts x; [:b, *x] } # => [], [:b], [1], [:b, 1], [1, 2], [:b, 1, 2], 3
@overload initialize(obj, method = :each, *args)
Creates a new Enumerator object, which can be used as an Enumerable.
In the first form, iteration is defined by the given block, in which a
“yielder” object, given as block parameter, can be used to yield a value by
calling the yield method (aliased as +<<+):
fib = Enumerator.new do |y| a = b = 1 loop do y << a a, b = b, a + b end end p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
In the second, deprecated, form, a generated Enumerator iterates over the given object using the given method with the given arguments passed. This form is left only for internal use.
Use of this form is discouraged. Use Kernel#enum_for or Kernel#to_enum instead.
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 117
def initialize(obj=NONE, meth=:each, *args, &block)
if block
obj = Generator.new(&block)
elsif obj == NONE
raise ArgumentError, "wrong number of arguments (given 0, expected 1+)"
end
@obj = obj
@meth = meth
@args = args
@fib = nil
@dst = nil
@lookahead = nil
@feedvalue = nil
@stop_exc = false
end
Creates an infinite enumerator from any block, just called over and over.
Result of the previous iteration is passed to the next one. If
initial is provided, it is passed to the first iteration, and
becomes the first element of the enumerator; if it is not provided, first
iteration receives nil, and its result becomes first element
of the iterator.
Raising StopIteration from the block stops an iteration.
Examples of usage:
Enumerator.produce(1, &:succ) # => enumerator of 1, 2, 3, 4, .... Enumerator.produce { rand(10) } # => infinite random number sequence ancestors = Enumerator.produce(node) { |prev| node = prev.parent or raise StopIteration } enclosing_section = ancestors.find { |n| n.type == :section }
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 580
def Enumerator.produce(init=NONE, &block)
raise ArgumentError, "no block given" if block.nil?
Enumerator.new do |y|
if init == NONE
val = nil
else
val = init
y.yield(val)
end
begin
while true
y.yield(val = block.call(val))
end
rescue StopIteration
# do nothing
end
end
end
# File mrbgems/mruby-enum-chain/mrblib/chain.rb, line 12
def +(other)
Chain.new(self, other)
end
Iterates over the block according to how this Enumerator was constructed. If no block and no arguments are given, returns self.
Array.new(3) #=> [nil, nil, nil] Array.new(3) { |i| i } #=> [0, 1, 2] Array.to_enum(:new, 3).to_a #=> [0, 1, 2] Array.to_enum(:new).each(3).to_a #=> [0, 1, 2] obj = Object.new def obj.each_arg(a, b=:b, *rest) yield a yield b yield rest :method_returned end enum = obj.to_enum :each_arg, :a, :x enum.each.to_a #=> [:a, :x, []] enum.each.equal?(enum) #=> true enum.each { |elm| elm } #=> :method_returned enum.each(:y, :z).to_a #=> [:a, :x, [:y, :z]] enum.each(:y, :z).equal?(enum) #=> false enum.each(:y, :z) { |elm| elm } #=> :method_returned
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 270
def each(*argv, &block)
obj = self
if 0 < argv.length
obj = self.dup
args = obj.args
if !args.empty?
args = args.dup
args.concat argv
else
args = argv.dup
end
obj.args = args
end
return obj unless block
enumerator_block_call(&block)
end
Same as #with_index, i.e. there is no starting offset.
If no block is given, a new Enumerator is returned that includes the index.
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 184
def each_with_index(&block)
with_index(0, &block)
end
Sets the value to be returned by the next yield inside e.
If the value is not set, the yield returns nil.
This value is cleared after being yielded.
# Array#map passes the array's elements to "yield" and collects the # results of "yield" as an array. # Following example shows that "next" returns the passed elements and # values passed to "feed" are collected as an array which can be # obtained by StopIteration#result. e = [1,2,3].map p e.next #=> 1 e.feed "a" p e.next #=> 2 e.feed "b" p e.next #=> 3 e.feed "c" begin e.next rescue StopIteration p $!.result #=> ["a", "b", "c"] end o = Object.new def o.each x = yield # (2) blocks p x # (5) => "foo" x = yield # (6) blocks p x # (8) => nil x = yield # (9) blocks p x # not reached w/o another e.next end e = o.to_enum e.next # (1) e.feed "foo" # (3) e.next # (4) e.next # (7) # (10)
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 520
def feed(value)
raise TypeError, "feed value already set" if @feedvalue
@feedvalue = value
nil
end
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 136
def initialize_copy(obj)
raise TypeError, "can't copy type #{obj.class}" unless obj.kind_of? Enumerator
raise TypeError, "can't copy execution context" if obj.fib
@obj = obj.obj
@meth = obj.meth
@args = obj.args
@fib = nil
@lookahead = nil
@feedvalue = nil
self
end
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 225
def inspect
if @args && @args.size > 0
args = @args.join(", ")
"#<#{self.class}: #{@obj.inspect}:#{@meth}(#{args})>"
else
"#<#{self.class}: #{@obj.inspect}:#{@meth}>"
end
end
Returns the next object in the enumerator, and move the internal position forward. When the position reached at the end, StopIteration is raised.
a = [1,2,3] e = a.to_enum p e.next #=> 1 p e.next #=> 2 p e.next #=> 3 p e.next #raises StopIteration
Note that enumeration sequence by next does not affect other
non-external enumeration methods, unless the underlying iteration methods
itself has side-effect
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 312
def next
next_values.__svalue
end
Returns the next object as an array in the enumerator, and move the internal position forward. When the position reached at the end, StopIteration is raised.
This method can be used to distinguish yield and yield
nil.
o = Object.new def o.each yield yield 1 yield 1, 2 yield nil yield [1, 2] end e = o.to_enum p e.next_values p e.next_values p e.next_values p e.next_values p e.next_values e = o.to_enum p e.next p e.next p e.next p e.next p e.next ## yield args next_values next # yield [] nil # yield 1 [1] 1 # yield 1, 2 [1, 2] [1, 2] # yield nil [nil] nil # yield [1, 2] [[1, 2]] [1, 2]
Note that next_values does not affect other non-external
enumeration methods unless underlying iteration method itself has
side-effect
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 360
def next_values
if @lookahead
vs = @lookahead
@lookahead = nil
return vs
end
raise @stop_exc if @stop_exc
curr = Fiber.current
if !@fib || !@fib.alive?
@dst = curr
@fib = Fiber.new do
result = each do |*args|
feedvalue = nil
Fiber.yield args
if @feedvalue
feedvalue = @feedvalue
@feedvalue = nil
end
feedvalue
end
@stop_exc = StopIteration.new "iteration reached an end"
@stop_exc.result = result
Fiber.yield nil
end
@lookahead = nil
end
vs = @fib.resume curr
if @stop_exc
@fib = nil
@dst = nil
@lookahead = nil
@feedvalue = nil
raise @stop_exc
end
vs
end
Returns the next object in the enumerator, but doesn't move the internal position forward. If the position is already at the end, StopIteration is raised.
a = [1,2,3] e = a.to_enum p e.next #=> 1 p e.peek #=> 2 p e.peek #=> 2 p e.peek #=> 2 p e.next #=> 2 p e.next #=> 3 p e.next #raises StopIteration
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 420
def peek
peek_values.__svalue
end
Returns the next object as an array, similar to #next_values, but doesn't move the internal position forward. If the position is already at the end, StopIteration is raised.
o = Object.new def o.each yield yield 1 yield 1, 2 end e = o.to_enum p e.peek_values #=> [] e.next p e.peek_values #=> [1] p e.peek_values #=> [1] e.next p e.peek_values #=> [1, 2] e.next p e.peek_values # raises StopIteration
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 450
def peek_values
if @lookahead.nil?
@lookahead = next_values
end
@lookahead.dup
end
Rewinds the enumeration sequence to the beginning.
If the enclosed object responds to a “rewind” method, it is called.
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 465
def rewind
@obj.rewind if @obj.respond_to? :rewind
@fib = nil
@dst = nil
@lookahead = nil
@feedvalue = nil
@stop_exc = false
self
end
Iterates the given block for each element with an index, which starts from
offset. If no block is given, returns a new Enumerator that includes the index, starting
from offset
offset
the starting index to use
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 159
def with_index(offset=0, &block)
return to_enum :with_index, offset unless block
if offset.nil?
offset = 0
else
offset = offset.__to_int
end
n = offset - 1
enumerator_block_call do |*i|
n += 1
block.call i.__svalue, n
end
end
Iterates the given block for each element with an arbitrary object,
obj, and returns obj
If no block is given, returns a new Enumerator.
@example
to_three = Enumerator.new do |y| 3.times do |x| y << x end end to_three_with_string = to_three.with_object("foo") to_three_with_string.each do |x,string| puts "#{string}: #{x}" end # => foo:0 # => foo:1 # => foo:2
# File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 216
def with_object(object, &block)
return to_enum(:with_object, object) unless block
enumerator_block_call do |i|
block.call [i,object]
end
object
end