Enumerator::Lazy
class Enumerator::Lazy
Public Class Methods
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
static VALUE lazy_super(int argc, VALUE *argv, VALUE lazy) { return enumerable_lazy(rb_call_super(argc, argv)); }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_map_func, 0), Qnil, lazy_receiver_size); }
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_func, 0), Qnil, 0); }
static VALUE lazy_drop(VALUE obj, VALUE n) { long len = NUM2LONG(n); if (len < 0) { rb_raise(rb_eArgError, "attempt to drop negative size"); } return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_drop_func, n), rb_ary_new3(1, n), lazy_drop_size); }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_drop_while_func, 0), Qnil, 0); }
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; }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_select_func, 0), Qnil, 0); }
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_func, 0), Qnil, 0); }
static VALUE lazy_grep(VALUE obj, VALUE pattern) { return lazy_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, rb_block_given_p() ? lazy_grep_iter : lazy_grep_func, pattern), rb_ary_new3(1, pattern), 0); }
static VALUE lazy_lazy(VALUE obj) { return obj; }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_map_func, 0), Qnil, lazy_receiver_size); }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_reject_func, 0), Qnil, 0); }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_select_func, 0), Qnil, 0); }
static VALUE lazy_super(int argc, VALUE *argv, VALUE lazy) { return enumerable_lazy(rb_call_super(argc, argv)); }
static VALUE lazy_super(int argc, VALUE *argv, VALUE lazy) { return enumerable_lazy(rb_call_super(argc, argv)); }
static VALUE lazy_super(int argc, VALUE *argv, VALUE lazy) { return enumerable_lazy(rb_call_super(argc, argv)); }
static VALUE lazy_take(VALUE obj, VALUE n) { long len = NUM2LONG(n); VALUE lazy; if (len < 0) { rb_raise(rb_eArgError, "attempt to take negative size"); } if (len == 0) { VALUE len = INT2FIX(0); lazy = lazy_to_enum_i(obj, sym_cycle, 1, &len, 0); } else { lazy = rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_take_func, n); } return lazy_set_method(lazy, rb_ary_new3(1, n), lazy_take_size); }
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_set_method(rb_block_call(rb_cLazy, id_new, 1, &obj, lazy_take_while_func, 0), Qnil, 0); }
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; }
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 %s (must respond to :each)", rb_obj_classname(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); }
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.