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(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 condition
URLS.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.

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.

.product

Generates a new enumerator object that generates a Cartesian product of given enumerable objects.

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?

Returns whether every element meets a given criterion.

#any?

Returns whether any element meets a given criterion.

#chain

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

#chunk

Each element in the returned enumerator is a 2-element array consisting of:

#chunk_while

Creates an enumerator for each chunked elements.

#collect

Alias for Enumerable#map.

#collect_concat
#compact

Returns an array of all non-nil elements:

#count

Returns the count of elements, based on an argument or block criterion, if given.

#cycle

When called with positive integer argument n and a block, calls the block with each element, then does so again, until it has done so n times; returns nil:

#detect

Alias for Enumerable#find.

#drop

For positive integer n, returns an array containing all but the first n elements:

#drop_while

Calls the block with successive elements as long as the block returns a truthy value; returns an array of all elements after that point:

#each_cons

Calls the block with each successive overlapped n-tuple of elements; returns self:

#each_entry

Calls the given block with each element, converting multiple values from yield to an array; returns self:

#each_slice

Calls the block with each successive disjoint n-tuple of elements; returns self:

#each_with_index

Invoke self.each with *args.

#each_with_object

Calls the block once for each element, passing both the element and the given object:

#entries

Alias for Enumerable#to_a.

#filter

Returns an array containing elements selected by the block.

#filter_map

Returns an array containing truthy elements returned by the block.

#find

Returns the first element for which the block returns a truthy value.

#find_all
#find_index

Returns the index of the first element that meets a specified criterion, or nil if no such element is found.

#first

Returns the first element or elements.

#flat_map

Returns an array of flattened objects returned by the block.

#grep

Returns an array of objects based elements of self that match the given pattern.

#grep_v

Returns an array of objects based on elements of self that don’t match the given pattern.

#group_by

With a block given returns a hash:

#include?
#inject

Returns the result of applying a reducer to an initial value and the first element of the ::Enumerable.

#lazy

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

#map

Returns an array of objects returned by the block.

#max

Returns the element with the maximum element according to a given criterion.

#max_by

Returns the elements for which the block returns the maximum values.

#member?

Returns whether for any element object == element:

#min

Returns the element with the minimum element according to a given criterion.

#min_by

Returns the elements for which the block returns the minimum values.

#minmax

Returns a 2-element array containing the minimum and maximum elements according to a given criterion.

#minmax_by

Returns a 2-element array containing the elements for which the block returns minimum and maximum values:

#none?

Returns whether no element meets a given criterion.

#one?

Returns whether exactly one element meets a given criterion.

#partition

With a block given, returns an array of two arrays:

#reduce
#reject

Returns an array of objects rejected by the block.

#reverse_each

With a block given, calls the block with each element, but in reverse order; returns self:

#select
#slice_after

Creates an enumerator for each chunked elements.

#slice_before

With argument pattern, returns an enumerator that uses the pattern to partition elements into arrays (“slices”).

#slice_when

Creates an enumerator for each chunked elements.

#sort

Returns an array containing the sorted elements of self.

#sort_by

With a block given, returns an array of elements of self, sorted according to the value returned by the block for each element.

#sum

With no block given, returns the sum of initial_value and the elements:

#take

For non-negative integer n, returns the first n elements:

#take_while

Calls the block with successive elements as long as the block returns a truthy value; returns an array of all elements up to that point:

#tally

When argument hash is not given, returns a new hash whose keys are the distinct elements in self; each integer value is the count of occurrences of each element:

#to_a

Returns an array containing the items in self:

#to_h

When self consists of 2-element arrays, returns a hash each of whose entries is the key-value pair formed from one of those arrays:

#to_set

Makes a set from the enumerable object with given arguments.

#uniq

With no block, returns a new array containing only unique elements; the array has no two elements e0 and e1 such that e0.eql?(e1):

#zip

With no block given, returns a new array new_array of size self.size whose elements are arrays.

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 1802

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

Alias for Enumerable#collect.

#_enumerable_collect_concat

#_enumerable_drop(n) ⇒ Array

Alias for Enumerable#drop.

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

#_enumerable_filter

Alias for Enumerable#filter.

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

#_enumerable_find_all

Alias for Enumerable#find_all.

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

Alias for Enumerable#flat_map.

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

Alias for Enumerable#grep.

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

Alias for Enumerable#grep_v.

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

Alias for Enumerable#map.

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

Alias for Enumerable#reject.

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

Alias for Enumerable#select.

#_enumerable_take(n) ⇒ Array

Alias for Enumerable#take.

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

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

Alias for Enumerable#uniq.

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

#_enumerable_zip(*other_enums) ⇒ Array #_enumerable_zip(*other_enums) {|array| ... } ⇒ nil

Alias for Enumerable#zip.

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

[ GitHub ]

  
# File 'enumerator.c', line 2839

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

[ GitHub ]

#compact

[ GitHub ]

  
# File 'enumerator.c', line 2709

static VALUE
lazy_compact(VALUE obj)
{
    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_compact_funcs);
}

#drop(n)

[ GitHub ]

  
# File 'enumerator.c', line 2584

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 2006

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 ]

#flat_map

[ GitHub ]

#to_aArray #forceArray

Alias for #to_a.

#grep

[ GitHub ]

#grep_v

[ GitHub ]

#lazyLazy

Returns self.

[ GitHub ]

  
# File 'enumerator.c', line 2852

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 2494

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

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

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 1974

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, rb_keyword_given_p());
    if (rb_block_given_p()) {
        RB_OBJ_WRITE(lazy, &enumerator_ptr(lazy)->size, rb_block_proc());
    }
    return lazy;
}

#uniq

[ GitHub ]

#zip

[ GitHub ]