In Files

  • mrbgems/mruby-enum-chain/mrblib/chain.rb
  • mrbgems/mruby-enum-chain/test/enum_chain.rb
  • mrbgems/mruby-enum-lazy/mrblib/lazy.rb
  • mrbgems/mruby-enum-lazy/test/lazy.rb
  • mrbgems/mruby-enumerator/mrblib/enumerator.rb
  • mrbgems/mruby-enumerator/test/enumerator.rb

Enumerator

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

Attributes

args[RW]
fib[R]
meth[RW]
obj[RW]

Public Class Methods

new(obj=NONE, meth=:each, *args, &block) click to toggle source

@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
            
produce(initial = nil) { |val| } → enumerator click to toggle source

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
            

Public Instance Methods

+(other) click to toggle source
 
               # File mrbgems/mruby-enum-chain/mrblib/chain.rb, line 12
def +(other)
  Chain.new(self, other)
end
            
each { |elm| block } → obj click to toggle source
each → enum
each(*appending_args) { |elm| block } → obj
each(*appending_args) → an_enumerator

Iterates over the block according to how this Enumerator was constructed. If no block and no arguments are given, returns self.

Examples

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
            
each_with_index {|(*args), idx| ... } click to toggle source
each_with_index

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
            
feed obj → nil click to toggle source

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
            
initialize_copy(obj) click to toggle source
 
               # 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
            
inspect() click to toggle source
 
               # 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
            
next → object click to toggle source

Returns the next object in the enumerator, and move the internal position forward. When the position reached at the end, StopIteration is raised.

Example

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
            
next_values → array click to toggle source

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.

Example

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
            
peek → object click to toggle source

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.

Example

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
            
peek_values → array click to toggle source

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.

Example

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
            
rewind → e click to toggle source

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
            
with_index(offset = 0) {|(*args), idx| ... } click to toggle source
with_index(offset = 0)

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
            
each_with_object(obj) {|(*args), obj| ... } click to toggle source
each_with_object(obj)
with_object(obj) {|(*args), obj| ... }
with_object(obj)

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
            

Private Instance Methods

enumerator_block_call(&block) click to toggle source
 
               # File mrbgems/mruby-enumerator/mrblib/enumerator.rb, line 287
def enumerator_block_call(&block)
  @obj.__send__ @meth, *@args, &block
end