Class: Enumerator

Inherits:
Object show all
Includes:
Enumerable
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.

  • Object#to_enum

  • Object#enum_for

  • Enumerator.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, Enumerator#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

Direct Known Subclasses

ArithmeticSequence, Chain, Lazy

Defined Under Namespace

Classes: ArithmeticSequence, Chain, Generator, Lazy, Producer, Yielder

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Enumerable

#all?, #any?, #chain, #chunk, #chunk_while, #collect, #collect_concat, #count, #cycle, #detect, #drop, #drop_while, #each_cons, #each_entry, #each_slice, #entries, #filter, #filter_map, #find, #find_all, #find_index, #first, #flat_map, #grep, #grep_v, #group_by, #include?, #inject, #lazy, #map, #max, #max_by, #member?, #min, #min_by, #minmax, #minmax_by, #none?, #one?, #partition, #reduce, #reject, #reverse_each, #select, #slice_after, #slice_before, #slice_when, #sort, #sort_by, #sum, #take, #take_while, #tally, #to_a, #to_h, #uniq, #zip

Constructor Details

#new(size = nil) {|yielder| ... } ⇒ Object #new(obj, method = :each, *args) ⇒ Object

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 Enumerator#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

Overloads:

  • #new(size = nil) {|yielder| ... } ⇒ Object

    Yields:

    • (yielder)


454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'enumerator.c', line 454

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 = PASS_KW_SPLAT;
    }

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

Class Method Details

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

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, 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"]

Yields:

  • (prev)


3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
# File 'enumerator.c', line 3003

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) ⇒ Object

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]


3298
3299
3300
3301
3302
3303
3304
# File 'enumerator.c', line 3298

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) ⇒ Object

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

Overloads:



607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
# File 'enumerator.c', line 607

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| ... } ⇒ Object #each_with_indexObject

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.

Overloads:

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

    Yields:

    • ((*args), idx)


687
688
689
690
691
# File 'enumerator.c', line 687

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

#each_with_object(obj) {|(*args), obj| ... } ⇒ Object #each_with_object(obj) ⇒ Object #with_object(obj) {|(*args), obj| ... } ⇒ Object #with_object(obj) ⇒ 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

Overloads:

  • #each_with_object(obj) {|(*args), obj| ... } ⇒ Object

    Yields:

    • ((*args), obj)
  • #with_object(obj) {|(*args), obj| ... } ⇒ Object

    Yields:

    • ((*args), obj)


731
732
733
734
735
736
737
738
# File 'enumerator.c', line 731

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;
}

#objnil

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)

Returns:

  • (nil)


1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
# File 'enumerator.c', line 1032

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) ⇒ Object

:nodoc:



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
# File 'enumerator.c', line 490

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.

Returns:



1201
1202
1203
1204
1205
# File 'enumerator.c', line 1201

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

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

Returns:



905
906
907
908
909
910
# File 'enumerator.c', line 905

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.

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, e.g. IO#each_line.

Returns:



848
849
850
851
852
853
854
855
856
857
858
859
860
861
# File 'enumerator.c', line 848

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.

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

Returns:



979
980
981
982
983
984
# File 'enumerator.c', line 979

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 Enumerator#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

Returns:



951
952
953
954
955
# File 'enumerator.c', line 951

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

#rewindObject

Rewinds the enumeration sequence to the beginning.

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



1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
# File 'enumerator.c', line 1054

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

Returns:



1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
# File 'enumerator.c', line 1218

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| ... } ⇒ Object #with_index(offset = 0) ⇒ Object

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

Overloads:

  • #with_index(offset = 0) {|(*args), idx| ... } ⇒ Object

    Yields:

    • ((*args), idx)


666
667
668
669
670
671
672
673
674
675
# File 'enumerator.c', line 666

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| ... } ⇒ Object #each_with_object(obj) ⇒ Object #with_object(obj) {|(*args), obj| ... } ⇒ Object #with_object(obj) ⇒ 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

Overloads:

  • #each_with_object(obj) {|(*args), obj| ... } ⇒ Object

    Yields:

    • ((*args), obj)
  • #with_object(obj) {|(*args), obj| ... } ⇒ Object

    Yields:

    • ((*args), obj)


731
732
733
734
735
736
737
738
# File 'enumerator.c', line 731

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;
}