1. Enumerator::
  2. Lazy

class Enumerator::Lazy

Enumerator::Lazy is a special type ofEnumerator, that allows constructing chains of operations without evaluating them immediately, and evaluating values on as-needed basis. In order to do so it redefines most ofEnumerable methods so that they just construct another lazy enumerator.

Enumerator::Lazy can be constructed from anyEnumerable with theEnumerable#lazy method.

lazy = (1..Float::INFINITY).lazy.select(&:odd?).drop(10).take_while {|i|i<30 }# => #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:select>:drop(10)>:take_while>

The real enumeration is performed when any non-redefinedEnumerable method is called, likeEnumerable#first orEnumerable#to_a (the latter is aliased asforce for more semantic code):

lazy.first(2)#=> [21, 23]lazy.force#=> [21, 23, 25, 27, 29]

Note that mostEnumerable methods that could be called with or without a block, onEnumerator::Lazy will always require a block:

[1,2,3].map#=> #<Enumerator: [1, 2, 3]:map>[1,2,3].lazy.map# ArgumentError: tried to call lazy map without a block

This class allows idiomatic calculations on long or infinite sequences, as well as chaining of calculations without constructing intermediate arrays.

Example for working with a slowly calculated sequence:

require'open-uri'# This will fetch all URLs before selecting# necessary dataURLS.map {|u|JSON.parse(URI.open(u).read) }  .select {|data|data.key?('stats') }  .first(5)# This will fetch URLs one-by-one, only till# there is enough data to satisfy the conditionURLS.lazy.map {|u|JSON.parse(URI.open(u).read) }  .select {|data|data.key?('stats') }  .first(5)

Ending a chain with “.eager” generates a non-lazy enumerator, which is suitable for returning or passing to another method that expects a normal enumerator.

defactive_itemsgroups    .lazy    .flat_map(&:items)    .reject(&:disabled)    .eagerend# This works lazily; if a checked item is found, it stops# iteration and does not look into remaining groups.first_checked =active_items.find(&:checked)# This returns an array of items like a normal enumerator does.all_checked =active_items.select(&:checked)

Public Class Methods

Source
static VALUElazy_initialize(int argc, VALUE *argv, VALUE self){    VALUE obj, size = Qnil;    VALUE generator;    rb_check_arity(argc, 1, 2);    LAZY_NEED_BLOCK(new);    obj = argv[0];    if (argc > 1) {        size = argv[1];    }    generator = generator_allocate(rb_cGenerator);    rb_block_call(generator, id_initialize, 0, 0, lazy_init_block_i, obj);    enumerator_init(self, generator, sym_each, 0, 0, 0, size, 0);    rb_ivar_set(self, id_receiver, obj);    return self;}

Creates a newLazy enumerator. When the enumerator is actually enumerated (e.g. by callingforce),obj will be enumerated and each value passed to the given block. The block can yield values back usingyielder. For example, to create a “filter+map” enumerator:

deffilter_map(sequence)Lazy.new(sequence)do|yielder,*values|result =yield*valuesyielder<<resultifresultendendfilter_map(1..Float::INFINITY) {|i|i*iifi.even?}.first(5)#=> [4, 16, 36, 64, 100]

Public Instance Methods

LikeEnumerable#map, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.map {|i|i**2 }#=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>(1..Float::INFINITY).lazy.map {|i|i**2 }.first(3)#=> [1, 4, 9]
Alias for:collect

Returns a new lazy enumerator with the concatenated results of runningblock once for every element in the lazy enumerator.

["foo","bar"].lazy.flat_map {|i|i.each_char.lazy}.force#=> ["f", "o", "o", "b", "a", "r"]

A valuex returned byblock is decomposed if either of the following conditions is true:

  • x responds to both each and force, which means thatx is a lazy enumerator.

  • x is an array or responds to to_ary.

Otherwise,x is contained as-is in the return value.

[{a:1}, {b:2}].lazy.flat_map {|i|i}.force#=> [{:a=>1}, {:b=>2}]
Alias for:collect_concat
Alias for:drop
Alias for:drop_while

LikeEnumerable#select, but chains operation to be lazy-evaluated.

Alias for:filter
Alias for:filter_map

LikeEnumerable#select, but chains operation to be lazy-evaluated.

Alias for:find_all
Alias for:flat_map
Alias for:grep
Alias for:grep_v
Alias for:map
Alias for:reject
Alias for:select
Alias for:take
Alias for:take_while
Alias for:uniq
Alias for:zip
Source
endifstatic VALUElazy_super(int argc, VALUE *argv, VALUE lazy){    return enumerable_lazy(rb_call_super(argc, argv));}

LikeEnumerable#chunk, but chains operation to be lazy-evaluated.

Also aliased as:slice_before,slice_after,slice_when,chunk_while

LikeEnumerable#chunk_while, but chains operation to be lazy-evaluated.

Alias for:chunk

LikeEnumerable#map, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.map {|i|i**2 }#=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>(1..Float::INFINITY).lazy.map {|i|i**2 }.first(3)#=> [1, 4, 9]
Also aliased as:_enumerable_collect
Alias for:map

Returns a new lazy enumerator with the concatenated results of runningblock once for every element in the lazy enumerator.

["foo","bar"].lazy.flat_map {|i|i.each_char.lazy}.force#=> ["f", "o", "o", "b", "a", "r"]

A valuex returned byblock is decomposed if either of the following conditions is true:

  • x responds to both each and force, which means thatx is a lazy enumerator.

  • x is an array or responds to to_ary.

Otherwise,x is contained as-is in the return value.

[{a:1}, {b:2}].lazy.flat_map {|i|i}.force#=> [{:a=>1}, {:b=>2}]
Also aliased as:_enumerable_collect_concat
Alias for:flat_map
Source
static VALUElazy_compact(VALUE obj){    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_compact_funcs);}

LikeEnumerable#compact, but chains operation to be lazy-evaluated.

Source
static VALUElazy_drop(VALUE obj, VALUE n){    long len = NUM2LONG(n);    VALUE argv[2];    argv[0] = sym_each;    argv[1] = n;    if (len < 0) {        rb_raise(rb_eArgError, "attempt to drop negative size");    }    return lazy_add_method(obj, 2, argv, n, rb_ary_new3(1, n), &lazy_drop_funcs);}

LikeEnumerable#drop, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_drop
Source
static VALUElazy_drop_while(VALUE obj){    LAZY_NEED_BLOCK(drop_while);    return lazy_add_method(obj, 0, 0, Qfalse, Qnil, &lazy_drop_while_funcs);}

LikeEnumerable#drop_while, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_drop_while
Source
static VALUElazy_eager(VALUE self){    return enumerator_init(enumerator_allocate(rb_cEnumerator),                           self, sym_each, 0, 0, lazy_eager_size, Qnil, 0);}

Returns a non-lazyEnumerator converted from the lazy enumerator.

Similar toObject#to_enum, except it returns a lazy enumerator. This makes it easy to defineEnumerable methods that will naturally remain lazy if called from a lazy enumerator.

For example, continuing from the example inObject#to_enum:

# See Object#to_enum for the definition of repeatr =1..Float::INFINITYr.repeat(2).first(5)# => [1, 1, 2, 2, 3]r.repeat(2).class# => Enumeratorr.repeat(2).map{|n|n**2}.first(5)# => endless loop!# works naturally on lazy enumerator:r.lazy.repeat(2).class# => Enumerator::Lazyr.lazy.repeat(2).map{|n|n**2}.first(5)# => [1, 1, 4, 4, 9]
Alias for:to_enum

LikeEnumerable#select, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_filter
Alias for:select
Source
static VALUElazy_filter_map(VALUE obj){    LAZY_NEED_BLOCK(filter_map);    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_filter_map_funcs);}

LikeEnumerable#filter_map, but chains operation to be lazy-evaluated.

(1..).lazy.filter_map {|i|i*2ifi.even? }.first(5)#=> [4, 8, 12, 16, 20]
Also aliased as:_enumerable_filter_map

LikeEnumerable#select, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_find_all
Alias for:select
Source
static VALUElazy_flat_map(VALUE obj){    LAZY_NEED_BLOCK(flat_map);    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs);}

Returns a new lazy enumerator with the concatenated results of runningblock once for every element in the lazy enumerator.

["foo","bar"].lazy.flat_map {|i|i.each_char.lazy}.force#=> ["f", "o", "o", "b", "a", "r"]

A valuex returned byblock is decomposed if either of the following conditions is true:

  • x responds to both each and force, which means thatx is a lazy enumerator.

  • x is an array or responds to to_ary.

Otherwise,x is contained as-is in the return value.

[{a:1}, {b:2}].lazy.flat_map {|i|i}.force#=> [{:a=>1}, {:b=>2}]
Also aliased as:collect_concat,_enumerable_flat_map

Expandslazy enumerator to an array. SeeEnumerable#to_a.

Alias for:to_a
Source
static VALUElazy_grep(VALUE obj, VALUE pattern){    const lazyenum_funcs *const funcs = rb_block_given_p() ?        &lazy_grep_iter_funcs : &lazy_grep_funcs;    return lazy_add_method(obj, 0, 0, pattern, rb_ary_new3(1, pattern), funcs);}

LikeEnumerable#grep, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_grep
Source
static VALUElazy_grep_v(VALUE obj, VALUE pattern){    const lazyenum_funcs *const funcs = rb_block_given_p() ?        &lazy_grep_v_iter_funcs : &lazy_grep_v_funcs;    return lazy_add_method(obj, 0, 0, pattern, rb_ary_new3(1, pattern), funcs);}

LikeEnumerable#grep_v, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_grep_v
Source
static VALUElazy_lazy(VALUE obj){    return obj;}

Returns self.

Source
static VALUElazy_map(VALUE obj){    LAZY_NEED_BLOCK(map);    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_map_funcs);}

LikeEnumerable#map, but chains operation to be lazy-evaluated.

(1..Float::INFINITY).lazy.map {|i|i**2 }#=> #<Enumerator::Lazy: #<Enumerator::Lazy: 1..Infinity>:map>(1..Float::INFINITY).lazy.map {|i|i**2 }.first(3)#=> [1, 4, 9]
Also aliased as:collect,_enumerable_map
Source
static VALUElazy_reject(VALUE obj){    LAZY_NEED_BLOCK(reject);    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_reject_funcs);}

LikeEnumerable#reject, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_reject
Source
static VALUElazy_select(VALUE obj){    LAZY_NEED_BLOCK(select);    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_select_funcs);}

LikeEnumerable#select, but chains operation to be lazy-evaluated.

Also aliased as:find_all,filter,_enumerable_select

LikeEnumerable#slice_after, but chains operation to be lazy-evaluated.

Alias for:chunk

LikeEnumerable#slice_before, but chains operation to be lazy-evaluated.

Alias for:chunk

LikeEnumerable#slice_when, but chains operation to be lazy-evaluated.

Alias for:chunk
Source
static VALUElazy_take(VALUE obj, VALUE n){    long len = NUM2LONG(n);    if (len < 0) {        rb_raise(rb_eArgError, "attempt to take negative size");    }    n = LONG2NUM(len);          /* no more conversion */    return lazy_add_method(obj, 0, 0, n, rb_ary_new3(1, n), &lazy_take_funcs);}

LikeEnumerable#take, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_take
Source
static VALUElazy_take_while(VALUE obj){    LAZY_NEED_BLOCK(take_while);    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_take_while_funcs);}

LikeEnumerable#take_while, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_take_while
Source
static VALUElazy_to_a(VALUE self){}

Expandslazy enumerator to an array. SeeEnumerable#to_a.

Also aliased as:force
Source
static VALUElazy_to_enum(int argc, VALUE *argv, VALUE self){    VALUE lazy, meth = sym_each, super_meth;    if (argc > 0) {        --argc;        meth = *argv++;    }    if (RTEST((super_meth = rb_hash_aref(lazy_use_super_method, meth)))) {        meth = super_meth;    }    lazy = lazy_to_enum_i(self, meth, argc, argv, 0, rb_keyword_given_p());    if (rb_block_given_p()) {        RB_OBJ_WRITE(lazy, &enumerator_ptr(lazy)->size, rb_block_proc());    }    return lazy;}

Similar toObject#to_enum, except it returns a lazy enumerator. This makes it easy to defineEnumerable methods that will naturally remain lazy if called from a lazy enumerator.

For example, continuing from the example inObject#to_enum:

# See Object#to_enum for the definition of repeatr =1..Float::INFINITYr.repeat(2).first(5)# => [1, 1, 2, 2, 3]r.repeat(2).class# => Enumeratorr.repeat(2).map{|n|n**2}.first(5)# => endless loop!# works naturally on lazy enumerator:r.lazy.repeat(2).class# => Enumerator::Lazyr.lazy.repeat(2).map{|n|n**2}.first(5)# => [1, 1, 4, 4, 9]
Also aliased as:enum_for
Source
static VALUElazy_uniq(VALUE obj){    const lazyenum_funcs *const funcs =        rb_block_given_p() ? &lazy_uniq_iter_funcs : &lazy_uniq_funcs;    return lazy_add_method(obj, 0, 0, Qnil, Qnil, funcs);}

LikeEnumerable#uniq, but chains operation to be lazy-evaluated.

Also aliased as:_enumerable_uniq
Source
static VALUElazy_with_index(int argc, VALUE *argv, VALUE obj){    VALUE memo;    rb_scan_args(argc, argv, "01", &memo);    if (NIL_P(memo))        memo = LONG2NUM(0);    return lazy_add_method(obj, 0, 0, memo, rb_ary_new_from_values(1, &memo), &lazy_with_index_funcs);}

If a block is given, returns a lazy enumerator that will iterate over the given block for each element with an index, which starts fromoffset, and returns a lazy enumerator that yields the same values (without the index).

If a block is not given, returns a new lazy enumerator that includes the index, starting fromoffset.

offset

the starting index to use

SeeEnumerator#with_index.

Source
static VALUElazy_zip(int argc, VALUE *argv, VALUE obj){    VALUE ary, v;    long i;    const lazyenum_funcs *funcs = &lazy_zip_funcs[1];    if (rb_block_given_p()) {        return rb_call_super(argc, argv);    }    ary = rb_ary_new2(argc);    for (i = 0; i < argc; i++) {        v = rb_check_array_type(argv[i]);        if (NIL_P(v)) {            for (; i < argc; i++) {                if (!rb_respond_to(argv[i], id_each)) {                    rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (must respond to :each)",                             rb_obj_class(argv[i]));                }            }            ary = rb_ary_new4(argc, argv);            funcs = &lazy_zip_funcs[0];            break;        }        rb_ary_push(ary, v);    }    return lazy_add_method(obj, 0, 0, ary, ary, funcs);}

LikeEnumerable#zip, but chains operation to be lazy-evaluated. However, if a block is given to zip, values are enumerated immediately.

Also aliased as:_enumerable_zip

Private Instance Methods

Source
static VALUEenumerator_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));}

Iterates the given block for each element with an index, which starts fromoffset. If no block is given, returns a newEnumerator that includes the index, starting fromoffset

offset

the starting index to use