123456789_123456789_123456789_123456789_123456789_

Class: Enumerator

Relationships & Source Files
Namespace Children
Classes:
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
self, ::Enumerable
Inherits: Object
Defined in: enumerator.c,
enumerator.c

Overview

A class which allows both internal and external iteration.

An Enumerator can be created by the following methods.

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

Note that enumeration sequence by #next, #next_values, #peek and #peek_values do not affect other non-external enumeration methods, unless the underlying iteration method itself has side-effect, e.g. IO#each_line.

Moreover, implementation typically uses fibers so performance could be slower and exception stacktraces different than expected.

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

Class Method Summary

Instance Method Summary

::Enumerable - Included

#all?

Passes each element of the collection to the given block.

#any?

Passes each element of the collection to the given block.

#chain

Returns an enumerator object generated from this enumerator and given enumerables.

#chunk

Enumerates over the items, chunking them together based on the return value of the block.

#chunk_while

Creates an enumerator for each chunked elements.

#collect

Alias for Enumerable#map.

#collect_concat
#count

Returns the number of items in enum through enumeration.

#cycle

Calls block for each element of enum repeatedly n times or forever if none or nil is given.

#detect

Alias for Enumerable#find.

#drop

Drops first n elements from enum, and returns rest elements in an array.

#drop_while

Drops elements up to, but not including, the first element for which the block returns nil or false and returns an array containing the remaining elements.

#each_cons

Iterates the given block for each array of consecutive <n> elements.

#each_entry

Calls block once for each element in self, passing that element as a parameter, converting multiple values from yield to an array.

#each_slice

Iterates the given block for each slice of <n> elements.

#each_with_index

Calls block with two arguments, the item and its index, for each item in enum.

#each_with_object

Iterates the given block for each element with an arbitrary object given, and returns the initially given object.

#entries

Alias for Enumerable#to_a.

#filter

Returns an array containing all elements of enum for which the given block returns a true value.

#filter_map

Returns a new array containing the truthy results (everything except false or nil) of running the block for every element in enum.

#find

Passes each entry in enum to block.

#find_all
#find_index

Compares each entry in enum with value or passes to block.

#first

Returns the first element, or the first n elements, of the enumerable.

#flat_map

Returns a new array with the concatenated results of running block once for every element in enum.

#grep

Returns an array of every element in enum for which Pattern === element.

#grep_v

Inverted version of Enumerable#grep.

#group_by

Groups the collection by result of the block.

#include?
#inject

Combines all elements of enum by applying a binary operation, specified by a block or a symbol that names a method or operator.

#lazy

Returns an Lazy, which redefines most ::Enumerable methods to postpone enumeration and enumerate values only on an as-needed basis.

#map

Returns a new array with the results of running block once for every element in enum.

#max

Returns the object in enum with the maximum value.

#max_by

Returns the object in enum that gives the maximum value from the given block.

#member?

Returns true if any member of enum equals obj.

#min

Returns the object in enum with the minimum value.

#min_by

Returns the object in enum that gives the minimum value from the given block.

#minmax

Returns a two element array which contains the minimum and the maximum value in the enumerable.

#minmax_by

Returns a two element array containing the objects in enum that correspond to the minimum and maximum values respectively from the given block.

#none?

Passes each element of the collection to the given block.

#one?

Passes each element of the collection to the given block.

#partition

Returns two arrays, the first containing the elements of enum for which the block evaluates to true, the second containing the rest.

#reduce
#reject

Returns an array for all elements of enum for which the given block returns false.

#reverse_each

Builds a temporary array and traverses that array in reverse order.

#select
#slice_after

Creates an enumerator for each chunked elements.

#slice_before

Creates an enumerator for each chunked elements.

#slice_when

Creates an enumerator for each chunked elements.

#sort

Returns an array containing the items in enum sorted.

#sort_by

Sorts enum using a set of keys generated by mapping the values in enum through the given block.

#sum

Returns the sum of elements in an ::Enumerable.

#take

Returns first n elements from enum.

#take_while

Passes elements to the block until the block returns nil or false, then stops iterating and returns an array of all prior elements.

#tally

Tallies the collection, i.e., counts the occurrences of each element.

#to_a

Returns an array containing the items in enum.

#to_h

Returns the result of interpreting enum as a list of [key, value] pairs.

#uniq

Returns a new array by removing duplicate values in self.

#zip

Takes one element from enum and merges corresponding elements from each args.

Constructor Details

.new(size = nil) {|yielder| ... } .new(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

fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

The optional parameter can be used to specify how to calculate the size in a lazy fashion (see #size). It can either be a value or a callable object.

In the deprecated second form, a generated Enumerator iterates over the given object using the given method with the given arguments passed.

Use of this form is discouraged. Use Object#enum_for or Object#to_enum instead.

e = Enumerator.new(ObjectSpace, :each_object)
    #-> ObjectSpace.enum_for(:each_object)

e.select { |obj| obj.is_a?(Class) }  # => array of all classes
[ GitHub ]

  
# File 'enumerator.c', line 460

static VALUE
enumerator_initialize(int argc, VALUE *argv, VALUE obj)
{
    VALUE recv, meth = sym_each;
    VALUE size = Qnil;
    int kw_splat = 0;

    if (rb_block_given_p()) {
	rb_check_arity(argc, 0, 1);
	recv = generator_init(generator_allocate(rb_cGenerator), rb_block_proc());
	if (argc) {
            if (NIL_P(argv[0]) || rb_respond_to(argv[0], id_call) ||
                (RB_TYPE_P(argv[0], T_FLOAT) && RFLOAT_VALUE(argv[0]) == HUGE_VAL)) {
                size = argv[0];
            }
            else {
                size = rb_to_int(argv[0]);
            }
            argc = 0;
        }
    }
    else {
	rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
	rb_warn_deprecated("Enumerator.new without a block", "Object#to_enum");
	recv = *argv++;
	if (--argc) {
	    meth = *argv++;
	    --argc;
	}
        kw_splat = rb_keyword_given_p();
    }

    return enumerator_init(obj, recv, meth, argc, argv, 0, size, kw_splat);
}

Class Method Details

.produce(initial = nil) {|prev| ... } ⇒ Enumerator

Creates an infinite enumerator from any block, just called over and over. The 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, the first iteration receives nil, and its result becomes the first element of the iterator.

Raising StopIteration from the block stops an iteration.

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 }

Using .produce together with ::Enumerable methods like Enumerable#detect, Enumerable#slice_after, Enumerable#take_while can provide Enumerator-based alternatives for while and until cycles:

# Find next Tuesday
require "date"
Enumerator.produce(Date.today, &:succ).detect(&:tuesday?)

# Simple lexer:
require "strscan"
scanner = StringScanner.new("7+38/6")
PATTERN = %r{\d|[-/*]}
Enumerator.produce { scanner.scan(PATTERN) }.slice_after { scanner.eos? }.first
# => ["7", "+", "38", "/", "6"]
[ GitHub ]

  
# File 'enumerator.c', line 3013

static VALUE
enumerator_s_produce(int argc, VALUE *argv, VALUE klass)
{
    VALUE init, producer;

    if (!rb_block_given_p()) rb_raise(rb_eArgError, "no block given");

    if (rb_scan_args(argc, argv, "01", &init) == 0) {
        init = Qundef;
    }

    producer = producer_init(producer_allocate(rb_cEnumProducer), init, rb_block_proc());

    return rb_enumeratorize_with_size_kw(producer, sym_each, 0, 0, producer_size, RB_NO_KEYWORDS);
}

Instance Method Details

#+(enum) ⇒ Enumerator

Returns an enumerator object generated from this enumerator and a given enumerable.

e = (1..3).each + [4, 5]
e.to_a #=> [1, 2, 3, 4, 5]
[ GitHub ]

  
# File 'enumerator.c', line 3308

static VALUE
enumerator_plus(VALUE obj, VALUE eobj)
{
    VALUE enums = rb_ary_new_from_args(2, obj, eobj);

    return enum_chain_initialize(enum_chain_allocate(rb_cEnumChain), enums);
}

#each {|elm| ... } ⇒ Object #eachEnumerator #each(*appending_args) {|elm| ... } ⇒ Object #each(*appending_args) ⇒ Enumerator

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

Examples

"Hello, world!".scan(/\w+/)                     #=> ["Hello", "world"]
"Hello, world!".to_enum(:scan, /\w+/).to_a      #=> ["Hello", "world"]
"Hello, world!".to_enum(:scan).each(/\w+/).to_a #=> ["Hello", "world"]

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
[ GitHub ]

  
# File 'enumerator.c', line 613

static VALUE
enumerator_each(int argc, VALUE *argv, VALUE obj)
{
    if (argc > 0) {
	struct enumerator *e = enumerator_ptr(obj = rb_obj_dup(obj));
	VALUE args = e->args;
	if (args) {
#if SIZEOF_INT < SIZEOF_LONG
	    /* check int range overflow */
	    rb_long2int(RARRAY_LEN(args) + argc);
#endif
	    args = rb_ary_dup(args);
	    rb_ary_cat(args, argv, argc);
	}
	else {
	    args = rb_ary_new4(argc, argv);
	}
	e->args = args;
        e->size = Qnil;
        e->size_fn = 0;
    }
    if (!rb_block_given_p()) return obj;
    return enumerator_block_call(obj, 0, obj);
}

#each_with_index {|(*args), idx| ... } #each_with_index

Same as Enumerator#with_index(0), i.e. there is no starting offset.

If no block is given, a new Enumerator is returned that includes the index.

[ GitHub ]

  
# File 'enumerator.c', line 693

static VALUE
enumerator_each_with_index(VALUE obj)
{
    return enumerator_with_index(0, NULL, obj);
}

#each_with_object(obj) {|(*args), obj| ... } #each_with_object(obj) #with_object(obj) {|(*args), obj| ... } #with_object(obj)

Alias for #with_object.

#feed(obj) ⇒ nil

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)
[ GitHub ]

  
# File 'enumerator.c', line 1038

static VALUE
enumerator_feed(VALUE obj, VALUE v)
{
    struct enumerator *e = enumerator_ptr(obj);

    if (e->feedvalue != Qundef) {
	rb_raise(rb_eTypeError, "feed value already set");
    }
    e->feedvalue = v;

    return Qnil;
}

#initialize_copy(orig)

This method is for internal use only.
[ GitHub ]

  
# File 'enumerator.c', line 496

static VALUE
enumerator_init_copy(VALUE obj, VALUE orig)
{
    struct enumerator *ptr0, *ptr1;

    if (!OBJ_INIT_COPY(obj, orig)) return obj;
    ptr0 = enumerator_ptr(orig);
    if (ptr0->fib) {
	/* Fibers cannot be copied */
	rb_raise(rb_eTypeError, "can't copy execution context");
    }

    TypedData_Get_Struct(obj, struct enumerator, &enumerator_data_type, ptr1);

    if (!ptr1) {
	rb_raise(rb_eArgError, "unallocated enumerator");
    }

    ptr1->obj  = ptr0->obj;
    ptr1->meth = ptr0->meth;
    ptr1->args = ptr0->args;
    ptr1->fib  = 0;
    ptr1->lookahead  = Qundef;
    ptr1->feedvalue  = Qundef;
    ptr1->size  = ptr0->size;
    ptr1->size_fn  = ptr0->size_fn;

    return obj;
}

#inspectString

Creates a printable version of e.

[ GitHub ]

  
# File 'enumerator.c', line 1207

static VALUE
enumerator_inspect(VALUE obj)
{
    return rb_exec_recursive(inspect_enumerator, obj, 0);
}

#nextObject

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

See class-level notes about external iterators.

[ GitHub ]

  
# File 'enumerator.c', line 907

static VALUE
enumerator_next(VALUE obj)
{
    VALUE vs = enumerator_next_values(obj);
    return ary2sv(vs, 0);
}

#next_valuesArray

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.

See class-level notes about external iterators.

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]
[ GitHub ]

  
# File 'enumerator.c', line 852

static VALUE
enumerator_next_values(VALUE obj)
{
    struct enumerator *e = enumerator_ptr(obj);
    VALUE vs;

    if (e->lookahead != Qundef) {
        vs = e->lookahead;
        e->lookahead = Qundef;
        return vs;
    }

    return get_next_values(obj, e);
}

#peekObject

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.

See class-level notes about external iterators.

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.peek   #raises StopIteration
[ GitHub ]

  
# File 'enumerator.c', line 985

static VALUE
enumerator_peek(VALUE obj)
{
    VALUE vs = enumerator_peek_values(obj);
    return ary2sv(vs, 1);
}

#peek_valuesArray

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.

See class-level notes about external iterators.

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
[ GitHub ]

  
# File 'enumerator.c', line 955

static VALUE
enumerator_peek_values_m(VALUE obj)
{
    return rb_ary_dup(enumerator_peek_values(obj));
}

#rewinde

Rewinds the enumeration sequence to the beginning.

If the enclosed object responds to a “rewind” method, it is called.

[ GitHub ]

  
# File 'enumerator.c', line 1060

static VALUE
enumerator_rewind(VALUE obj)
{
    struct enumerator *e = enumerator_ptr(obj);

    rb_check_funcall(e->obj, id_rewind, 0, 0);

    e->fib = 0;
    e->dst = Qnil;
    e->lookahead = Qundef;
    e->feedvalue = Qundef;
    e->stop_exc = Qfalse;
    return obj;
}

#sizeInteger, ...

Returns the size of the enumerator, or nil if it can't be calculated lazily.

(1..100).to_a.permutation(4).size # => 94109400
loop.size # => Float::INFINITY
(1..100).drop_while.size # => nil
[ GitHub ]

  
# File 'enumerator.c', line 1224

static VALUE
enumerator_size(VALUE obj)
{
    struct enumerator *e = enumerator_ptr(obj);
    int argc = 0;
    const VALUE *argv = NULL;
    VALUE size;

    if (e->procs) {
	struct generator *g = generator_ptr(e->obj);
	VALUE receiver = rb_check_funcall(g->obj, id_size, 0, 0);
	long i = 0;

	for (i = 0; i < RARRAY_LEN(e->procs); i++) {
	    VALUE proc = RARRAY_AREF(e->procs, i);
	    struct proc_entry *entry = proc_entry_ptr(proc);
	    lazyenum_size_func *size_fn = entry->fn->size;
	    if (!size_fn) {
		return Qnil;
	    }
	    receiver = (*size_fn)(proc, receiver);
	}
	return receiver;
    }

    if (e->size_fn) {
	return (*e->size_fn)(e->obj, e->args, obj);
    }
    if (e->args) {
	argc = (int)RARRAY_LEN(e->args);
	argv = RARRAY_CONST_PTR(e->args);
    }
    size = rb_check_funcall_kw(e->size, id_call, argc, argv, e->kw_splat);
    if (size != Qundef) return size;
    return e->size;
}

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

[ GitHub ]

  
# File 'enumerator.c', line 672

static VALUE
enumerator_with_index(int argc, VALUE *argv, VALUE obj)
{
    VALUE memo;

    rb_check_arity(argc, 0, 1);
    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enumerator_enum_size);
    memo = (!argc || NIL_P(memo = argv[0])) ? INT2FIX(0) : rb_to_int(memo);
    return enumerator_block_call(obj, enumerator_with_index_i, (VALUE)MEMO_NEW(memo, 0, 0));
}

#each_with_object(obj) {|(*args), obj| ... } #each_with_object(obj) #with_object(obj) {|(*args), obj| ... } #with_object(obj)
Also known as: #each_with_object

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
[ GitHub ]

  
# File 'enumerator.c', line 737

static VALUE
enumerator_with_object(VALUE obj, VALUE memo)
{
    RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enumerator_enum_size);
    enumerator_block_call(obj, enumerator_with_object_i, memo);

    return memo;
}