Enumerator::Lazy is a special type ofEnumerator, that allows constructing chainsof operations without evaluating them immediately, and evaluating values onas-needed basis. In order to do so it redefines most ofEnumerable methods so that they justconstruct 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 isaliased asforce for more semanticcode):
lazy.first(2)#=> [21, 23]lazy.force#=> [21, 23, 25, 27, 29]
Note that mostEnumerable methods thatcould 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, aswell 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(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(open(u).read) } .select {|data|data.key?('stats') } .first(5)
Ending a chain with “.eager” generates a non-lazy enumerator, which issuitable for returning or passing to another method that expects a normalenumerator.
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)
Creates a newLazy enumerator. When the enumeratoris actually enumerated (e.g. by callingforce),obj
will beenumerated and each value passed to the given block. The block can yieldvalues 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]
static VALUElazy_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;}
LikeEnumerable#chunk, butchains operation to be lazy-evaluated.
static VALUElazy_super(int argc, VALUE *argv, VALUE lazy){ return enumerable_lazy(rb_call_super(argc, argv));}
LikeEnumerable#chunk_while,but chains operation to be lazy-evaluated.
static VALUElazy_super(int argc, VALUE *argv, VALUE lazy){ return enumerable_lazy(rb_call_super(argc, argv));}
LikeEnumerable#map, butchains 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]
static VALUElazy_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);}
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 ifeither 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}]
static VALUElazy_flat_map(VALUE obj){ if (!rb_block_given_p()) { rb_raise(rb_eArgError, "tried to call lazy flat_map without a block"); } return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs);}
LikeEnumerable#drop, butchains operation to be lazy-evaluated.
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_while,but chains operation to be lazy-evaluated.
static VALUElazy_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);}
Returns a non-lazyEnumerator convertedfrom the lazy enumerator.
static VALUElazy_eager(VALUE self){ return enumerator_init(enumerator_allocate(rb_cEnumerator), self, sym_each, 0, 0, lazy_eager_size, Qnil, 0);}
Similar toObject#to_enum,except it returns a lazy enumerator. This makes it easy to defineEnumerable methods that will naturally remainlazy 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]
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()) { enumerator_ptr(lazy)->size = rb_block_proc(); } return lazy;}
LikeEnumerable#select,but chains operation to be lazy-evaluated.
static VALUElazy_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);}
LikeEnumerable#filter_map,but chains operation to be lazy-evaluated.
(1..).lazy.filter_map { |i| i * 2 if i.even? }.first(5)#=> [4, 8, 12, 16, 20]
static VALUElazy_filter_map(VALUE obj){ if (!rb_block_given_p()) { rb_raise(rb_eArgError, "tried to call lazy filter_map without a block"); } return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_filter_map_funcs);}
LikeEnumerable#select,but chains operation to be lazy-evaluated.
static VALUElazy_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);}
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 ifeither 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}]
static VALUElazy_flat_map(VALUE obj){ if (!rb_block_given_p()) { rb_raise(rb_eArgError, "tried to call lazy flat_map without a block"); } return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs);}
LikeEnumerable#grep, butchains operation to be lazy-evaluated.
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_v,but chains operation to be lazy-evaluated.
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);}
Returns self.
static VALUElazy_lazy(VALUE obj){ return obj;}
LikeEnumerable#map, butchains 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]
static VALUElazy_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);}
LikeEnumerable#reject,but chains operation to be lazy-evaluated.
static VALUElazy_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);}
LikeEnumerable#select,but chains operation to be lazy-evaluated.
static VALUElazy_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);}
LikeEnumerable#slice_after,but chains operation to be lazy-evaluated.
static VALUElazy_super(int argc, VALUE *argv, VALUE lazy){ return enumerable_lazy(rb_call_super(argc, argv));}
LikeEnumerable#slice_before,but chains operation to be lazy-evaluated.
static VALUElazy_super(int argc, VALUE *argv, VALUE lazy){ return enumerable_lazy(rb_call_super(argc, argv));}
LikeEnumerable#slice_when,but chains operation to be lazy-evaluated.
static VALUElazy_super(int argc, VALUE *argv, VALUE lazy){ return enumerable_lazy(rb_call_super(argc, argv));}
LikeEnumerable#take, butchains operation to be lazy-evaluated.
static VALUElazy_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);}
LikeEnumerable#take_while,but chains operation to be lazy-evaluated.
static VALUElazy_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);}
Expandslazy
enumerator to an array. SeeEnumerable#to_a.
static VALUE lazy_to_a(VALUE self){}
Similar toObject#to_enum,except it returns a lazy enumerator. This makes it easy to defineEnumerable methods that will naturally remainlazy 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]
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()) { enumerator_ptr(lazy)->size = rb_block_proc(); } return lazy;}
LikeEnumerable#uniq, butchains operation to be lazy-evaluated.
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);}
If a block is given, iterates the given block for each element with anindex, which starts fromoffset
, and returns a lazy enumeratorthat yields the same values (without the index).
If a block is not given, returns a new lazy enumerator that includes theindex, starting fromoffset
.
offset
the starting index to use
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);}
LikeEnumerable#zip, butchains operation to be lazy-evaluated. However, if a block is given to zip,values are enumerated immediately.
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);}
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, startingfromoffset
offset
the starting index to use
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));}
This page was generated for Ruby 3.0.0
Generated with Ruby-doc Rdoc Generator 0.42.0.