# class Enumerator::Lazy

Lazy

### Public Class Methods

new(obj, size=nil) { |yielder, *values| ... } click to toggle source

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 method `filter_map` in both lazy and non-lazy fashions:

    module Enumerable
      def filter_map(&block)
        map(&block).compact
      end
    end

    class Enumerator::Lazy
      def filter_map
        Lazy.new(self) do |yielder, *values|
          result = yield *values
          yielder << result if result
        end
      end
    end

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

    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);
        rb_ivar_set(self, id_receiver, obj);

        return self;
    }

### Public Instance Methods

chunk(*args) click to toggle source

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

chunk_while(*args) click to toggle source

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

collect() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_map_funcs);
    }

collect_concat { |obj| block } → a_lazy_enumerator click to toggle source

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

    ["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:

    a) <i>x</i> responds to both each and force, which means that
       <i>x</i> is a lazy enumerator.
    b) <i>x</i> 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}]

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

drop(p1) click to toggle source

    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() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qfalse, Qnil, &lazy_drop_while_funcs);
    }

enum_for(method = :each, *args) → lazy_enum click to toggle source
enum_for(method = :each, *args){|*args| block} → lazy_enum

Similar to Kernel#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 Kernel#to_enum:

    # See Kernel#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]

    static VALUE
    lazy_to_enum(int argc, VALUE *argv, VALUE self)
    {
        VALUE lazy, meth = sym_each;

        if (argc > 0) {
            --argc;
            meth = *argv++;
        }
        lazy = lazy_to_enum_i(self, meth, argc, argv, 0);
        if (rb_block_given_p()) {
            enumerator_ptr(lazy)->size = rb_block_proc();
        }
        return lazy;
    }

find_all() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_select_funcs);
    }

flat_map { |obj| block } → a_lazy_enumerator click to toggle source

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

    ["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:

    a) <i>x</i> responds to both each and force, which means that
       <i>x</i> is a lazy enumerator.
    b) <i>x</i> 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}]

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

grep(p1) click to toggle source

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

grep_v(p1) click to toggle source

    static VALUE
    lazy_grep_v(VALUE obj, VALUE pattern)
    {
        return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
                                             rb_block_given_p() ?
                                             lazy_grep_v_iter : lazy_grep_v_func,
                                             pattern),
                               rb_ary_new3(1, pattern), 0);
    }

lazy() click to toggle source

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

map() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_map_funcs);
    }

reject() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_reject_funcs);
    }

select() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_select_funcs);
    }

slice_after(*args) click to toggle source

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

slice_before(*args) click to toggle source

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

slice_when(*args) click to toggle source

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

take(p1) click to toggle source

    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() click to toggle source

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

        return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_take_while_funcs);
    }

to_enum(method = :each, *args) → lazy_enum click to toggle source
to_enum(method = :each, *args) {|*args| block} → lazy_enum

Similar to Kernel#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 Kernel#to_enum:

    # See Kernel#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]

    static VALUE
    lazy_to_enum(int argc, VALUE *argv, VALUE self)
    {
        VALUE lazy, meth = sym_each;

        if (argc > 0) {
            --argc;
            meth = *argv++;
        }
        lazy = lazy_to_enum_i(self, meth, argc, argv, 0);
        if (rb_block_given_p()) {
            enumerator_ptr(lazy)->size = rb_block_proc();
        }
        return lazy;
    }

uniq() click to toggle source

    static VALUE
    lazy_uniq(VALUE obj)
    {
        rb_block_call_func *const func =
            rb_block_given_p() ? lazy_uniq_iter : lazy_uniq_func;
        return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj,
                                             func, 0),
                               0, 0);
    }

zip(*args) click to toggle source

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