123456789_123456789_123456789_123456789_123456789_

Class: Enumerator::Lazy

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::Enumerator
Instance Chain:
Inherits: Enumerator
Defined in: enumerator.c,
enumerator.c

Overview

Lazy is a special type of ::Enumerator, 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 of ::Enumerable methods so that they just construct another lazy enumerator.

Lazy can be constructed from any ::Enumerable with the Enumerable#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-redefined ::Enumerable method is called, like Enumerable#first or Enumerable#to_a (the latter is aliased as #force for more semantic code):

lazy.first(2)
#=> [21, 23]

lazy.force
#=> [21, 23, 25, 27, 29]

Note that most ::Enumerable methods that could be called with or without a block, on 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 data
URLS.map { |u| JSON.parse(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 condition
URLS.lazy.map { |u| JSON.parse(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.

def active_items
  groups
    .lazy
    .flat_map(&:items)
    .reject(&:disabled)
    .eager
end

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

Class Method Summary

::Enumerator - Inherited

.new

Creates a new ::Enumerator object, which can be used as an ::Enumerable.

.produce

Creates an infinite enumerator from any block, just called over and over.

Instance Method Summary

::Enumerator - Inherited

#+

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

#each

Iterates over the block according to how this ::Enumerator was constructed.

#each_with_index

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

#each_with_object

Alias for #with_object.

#feed

Sets the value to be returned by the next yield inside e.

#inspect

Creates a printable version of e.

#next

Returns the next object in the enumerator, and move the internal position forward.

#next_values

Returns the next object as an array in the enumerator, and move the internal position forward.

#peek

Returns the next object in the enumerator, but doesn’t move the internal position forward.

#peek_values

Returns the next object as an array, similar to #next_values, but doesn’t move the internal position forward.

#rewind

Rewinds the enumeration sequence to the beginning.

#size

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

#with_index

Iterates the given block for each element with an index, which starts from offset.

#with_object

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

#initialize_copy

::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(obj, size = nil) {|yielder, *values| ... }

Creates a new Lazy enumerator. When the enumerator is actually enumerated (e.g. by calling #force), obj will be enumerated and each value passed to the given block. The block can yield values back using yielder. For example, to create a “filter+map” enumerator:

def filter_map(sequence)
  Lazy.new(sequence) do |yielder, *values|
    result = yield *values
    yielder << result if result
  end
end

filter_map(1..Float::INFINITY) {|i| i*i if i.even?}.first(5)
#=> [4, 16, 36, 64, 100]
[ GitHub ]

  
# File 'enumerator.c', line 1756

static VALUE
lazy_initialize(int argc, VALUE *argv, VALUE self)
{
    VALUE obj, size = Qnil;
    VALUE generator;

    rb_check_arity(argc, 1, 2);
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy new without a block");
    }
    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;
}

Instance Method Details

#_enumerable_collect {|obj| ... } ⇒ Array #_enumerable_collectEnumerator

Alias for Enumerable#collect.

#_enumerable_collect_concat {|obj| ... } ⇒ Array #_enumerable_collect_concatEnumerator

#_enumerable_drop(n) ⇒ Array

Alias for Enumerable#drop.

#_enumerable_drop_while {|obj| ... } ⇒ Array #_enumerable_drop_whileEnumerator

#_enumerable_filter {|obj| ... } ⇒ Array #_enumerable_filterEnumerator

Alias for Enumerable#filter.

#_enumerable_filter_map {|obj| ... } ⇒ Array #_enumerable_filter_mapEnumerator

#_enumerable_find_all {|obj| ... } ⇒ Array #_enumerable_find_allEnumerator

Alias for Enumerable#find_all.

#_enumerable_flat_map {|obj| ... } ⇒ Array #_enumerable_flat_mapEnumerator

Alias for Enumerable#flat_map.

#_enumerable_grep(pattern) ⇒ Array #_enumerable_grep(pattern) {|obj| ... } ⇒ Array

Alias for Enumerable#grep.

#_enumerable_grep_v(pattern) ⇒ Array #_enumerable_grep_v(pattern) {|obj| ... } ⇒ Array

Alias for Enumerable#grep_v.

#_enumerable_map {|obj| ... } ⇒ Array #_enumerable_mapEnumerator

Alias for Enumerable#map.

#_enumerable_reject {|obj| ... } ⇒ Array #_enumerable_rejectEnumerator

Alias for Enumerable#reject.

#_enumerable_select {|obj| ... } ⇒ Array #_enumerable_selectEnumerator

Alias for Enumerable#select.

#_enumerable_take(n) ⇒ Array

Alias for Enumerable#take.

#_enumerable_take_while {|obj| ... } ⇒ Array #_enumerable_take_whileEnumerator

#_enumerable_uniqArray #_enumerable_uniq {|item| ... } ⇒ Array

Alias for Enumerable#uniq.

#_enumerable_with_index(offset = 0) {|(*args), idx| ... } (private) #_enumerable_with_index(offset = 0)

#_enumerable_zip(arg, ...) ⇒ an_array_of_array #_enumerable_zip(arg, ...) {|arr| ... } ⇒ nil

Alias for Enumerable#zip.

#chunk(*args) Also known as: #slice_before, #slice_after, #slice_when, #chunk_while

[ GitHub ]

  
# File 'enumerator.c', line 2760

static VALUE
lazy_super(int argc, VALUE *argv, VALUE lazy)
{
    return enumerable_lazy(rb_call_super(argc, argv));
}

#chunk_while(*args)

Alias for #chunk.

#collect

[ GitHub ]

#collect_concat {|obj| ... } ⇒ Lazy #flat_map {|obj| ... } ⇒ Lazy

Alias for #flat_map.

#drop(n)

[ GitHub ]

  
# File 'enumerator.c', line 2542

static VALUE
lazy_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);
}

#drop_while

[ GitHub ]

#eagerEnumerator

Returns a non-lazy ::Enumerator converted from the lazy enumerator.

[ GitHub ]

  
# File 'enumerator.c', line 1955

static VALUE
lazy_eager(VALUE self)
{
    return enumerator_init(enumerator_allocate(rb_cEnumerator),
                           self, sym_each, 0, 0, lazy_eager_size, Qnil, 0);
}

#to_enum(method = :each, *args) ⇒ Lazy #enum_for(method = :each, *args) ⇒ Lazy #to_enum(method = :each, *args) {|*args| ... } ⇒ Lazy #enum_for(method = :each, *args) {|*args| ... } ⇒ Lazy

Alias for #to_enum.

#filter

[ GitHub ]

#filter_map

[ GitHub ]

#find_all

[ GitHub ]

#collect_concat {|obj| ... } ⇒ Lazy #flat_map {|obj| ... } ⇒ Lazy
Also known as: #collect_concat

Returns a new lazy enumerator with the concatenated results of running block 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 value x returned by block is decomposed if either of the following conditions is true:

  • x responds to both each and force, which means that x 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}]
[ GitHub ]

  
# File 'enumerator.c', line 2100

static VALUE
lazy_flat_map(VALUE obj)
{
    if (!rb_block_given_p()) {
	rb_raise(rb_eArgError, "tried to call lazy flat_map without a block");
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 lazy_flat_map_proc, 0),
			   Qnil, 0);
}

#to_aArray #forceArray

Alias for #to_a.

#grep

[ GitHub ]

#grep_v

[ GitHub ]

#lazyLazy

Returns self.

[ GitHub ]

  
# File 'enumerator.c', line 2773

static VALUE
lazy_lazy(VALUE obj)
{
    return obj;
}

#map

[ GitHub ]

#reject

[ GitHub ]

#select

[ GitHub ]

#slice_after(*args)

Alias for #chunk.

#slice_before(*args)

Alias for #chunk.

#slice_when(*args)

Alias for #chunk.

#take(n)

[ GitHub ]

  
# File 'enumerator.c', line 2446

static VALUE
lazy_take(VALUE obj, VALUE n)
{
    long len = NUM2LONG(n);
    int argc = 0;
    VALUE argv[2];

    if (len < 0) {
	rb_raise(rb_eArgError, "attempt to take negative size");
    }

    if (len == 0) {
       argv[0] = sym_cycle;
       argv[1] = INT2NUM(0);
       argc = 2;
    }

    return lazy_add_method(obj, argc, argv, n, rb_ary_new3(1, n), &lazy_take_funcs);
}

#take_while

[ GitHub ]

#to_aArray #forceArray
Also known as: #force

Expands #lazy enumerator to an array. See Enumerable#to_a.

[ GitHub ]

  
# File 'enumerator.c', line 1787

static VALUE lazy_to_a(VALUE self)
{
}

#to_enum(method = :each, *args) ⇒ Lazy #enum_for(method = :each, *args) ⇒ Lazy #to_enum(method = :each, *args) {|*args| ... } ⇒ Lazy #enum_for(method = :each, *args) {|*args| ... } ⇒ Lazy
Also known as: #enum_for

Similar to Object#to_enum, except it returns a lazy enumerator. This makes it easy to define ::Enumerable methods that will naturally remain lazy if called from a lazy enumerator.

For example, continuing from the example in Object#to_enum:

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

  
# File 'enumerator.c', line 1923

static VALUE
lazy_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, PASS_KW_SPLAT);
    if (rb_block_given_p()) {
	enumerator_ptr(lazy)->size = rb_block_proc();
    }
    return lazy;
}

#uniq

[ GitHub ]

#zip(arg, ...) ⇒ Lazy #zip(arg, ...) {|arr| ... } ⇒ nil

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

[ GitHub ]

  
# File 'enumerator.c', line 2371

static VALUE
lazy_zip(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary, v;
    long i;
    rb_block_call_func *func = lazy_zip_arrays_func;

    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);
	    func = lazy_zip_func;
	    break;
	}
	rb_ary_push(ary, v);
    }

    return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
					 func, ary),
			   ary, lazy_receiver_size);
}