class Enumerator::Lazy
Enumerator::Lazy ist ein spezieller Typ von Enumerator, der das Erstellen von Operationsketten ermöglicht, ohne diese sofort auszuwerten, und Werte nach Bedarf auswertet. Dazu überschreibt er die meisten Methoden von Enumerable so, dass sie lediglich einen weiteren Lazy Enumerator erstellen.
Enumerator::Lazy kann aus jedem Enumerable mit der Methode Enumerable#lazy erstellt werden.
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>
Die eigentliche Enumeration erfolgt, wenn eine nicht überschriebene Methode von Enumerable aufgerufen wird, wie z.B. Enumerable#first oder Enumerable#to_a (letzteres ist als force für semantischeren Code als Alias definiert).
lazy.first(2) #=> [21, 23] lazy.force #=> [21, 23, 25, 27, 29]
Beachten Sie, dass die meisten Enumerable-Methoden, die mit oder ohne Block aufgerufen werden können, bei Enumerator::Lazy immer einen Block erfordern.
[1, 2, 3].map #=> #<Enumerator: [1, 2, 3]:map> [1, 2, 3].lazy.map # ArgumentError: tried to call lazy map without a block
Diese Klasse ermöglicht idiomatische Berechnungen auf langen oder unendlichen Sequenzen sowie das Verketten von Berechnungen, ohne Zwischenarrays zu erstellen.
Beispiel für die Arbeit mit einer langsam berechneten Sequenz
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)
Das Beenden einer Kette mit ".eager" erzeugt einen nicht-faulen Enumerator, der sich zum Zurückgeben oder Übergeben an eine andere Methode eignet, die einen normalen Enumerator erwartet.
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)
Öffentliche Klassenmethoden
Source
static VALUE
lazy_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE obj, size = Qnil;
VALUE generator;
rb_check_arity(argc, 1, 2);
LAZY_NEED_BLOCK(new);
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;
}
Erstellt einen neuen Lazy-Enumerator. Wenn der Enumerator tatsächlich durchlaufen wird (z.B. durch Aufruf von force), wird obj durchlaufen und jeder Wert an den angegebenen Block übergeben. Der Block kann Werte über yielder zurückgeben. Zum Beispiel, um einen "filter+map"-Enumerator zu erstellen.
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]
Öffentliche Instanzmethoden
Wie Enumerable#map, aber verknüpft Operationen zur verzögerten Auswertung.
(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]
Gibt einen neuen Lazy Enumerator mit den verketteten Ergebnissen der einmaligen Ausführung von block für jedes Element im Lazy Enumerator zurück.
["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force #=> ["f", "o", "o", "b", "a", "r"]
Ein Wert x, der von block zurückgegeben wird, wird zerlegt, wenn eine der folgenden Bedingungen erfüllt ist:
-
xantwortet sowohl auf each als auch auf force, was bedeutet, dassxein Lazy Enumerator ist. -
xist ein Array oder antwortet auf to_ary.
Andernfalls wird x unverändert in den Rückgabewert aufgenommen.
[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
Wie Enumerable#select, aber verknüpft Operationen zur verzögerten Auswertung.
Wie Enumerable#select, aber verknüpft Operationen zur verzögerten Auswertung.
Source
endif
static VALUE
lazy_super(int argc, VALUE *argv, VALUE lazy)
{
return enumerable_lazy(rb_call_super(argc, argv));
}
Wie Enumerable#chunk, aber verknüpft Operationen zur verzögerten Auswertung.
Wie Enumerable#chunk_while, aber verknüpft Operationen zur verzögerten Auswertung.
Wie Enumerable#map, aber verknüpft Operationen zur verzögerten Auswertung.
(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]
Gibt einen neuen Lazy Enumerator mit den verketteten Ergebnissen der einmaligen Ausführung von block für jedes Element im Lazy Enumerator zurück.
["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force #=> ["f", "o", "o", "b", "a", "r"]
Ein Wert x, der von block zurückgegeben wird, wird zerlegt, wenn eine der folgenden Bedingungen erfüllt ist:
-
xantwortet sowohl auf each als auch auf force, was bedeutet, dassxein Lazy Enumerator ist. -
xist ein Array oder antwortet auf to_ary.
Andernfalls wird x unverändert in den Rückgabewert aufgenommen.
[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
Source
static VALUE
lazy_compact(VALUE obj)
{
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_compact_funcs);
}
Wie Enumerable#compact, aber verknüpft Operationen zur verzögerten Auswertung.
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);
}
Wie Enumerable#drop, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_drop_while(VALUE obj)
{
LAZY_NEED_BLOCK(drop_while);
return lazy_add_method(obj, 0, 0, Qfalse, Qnil, &lazy_drop_while_funcs);
}
Wie Enumerable#drop_while, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_eager(VALUE self)
{
return enumerator_init(enumerator_allocate(rb_cEnumerator),
self, sym_each, 0, 0, lazy_eager_size, Qnil, 0);
}
Gibt einen nicht-faulen Enumerator zurück, der aus dem Lazy Enumerator konvertiert wurde.
Ähnlich wie Object#to_enum, gibt aber einen Lazy Enumerator zurück. Dies erleichtert die Definition von Enumerable-Methoden, die natürlich faul bleiben, wenn sie von einem Lazy Enumerator aus aufgerufen werden.
Zum Beispiel, fortgesetzt aus dem Beispiel 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]
Wie Enumerable#select, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_filter_map(VALUE obj)
{
LAZY_NEED_BLOCK(filter_map);
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_filter_map_funcs);
}
Wie Enumerable#filter_map, aber verknüpft Operationen zur verzögerten Auswertung.
(1..).lazy.filter_map { |i| i * 2 if i.even? }.first(5) #=> [4, 8, 12, 16, 20]
Wie Enumerable#select, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_flat_map(VALUE obj)
{
LAZY_NEED_BLOCK(flat_map);
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs);
}
Gibt einen neuen Lazy Enumerator mit den verketteten Ergebnissen der einmaligen Ausführung von block für jedes Element im Lazy Enumerator zurück.
["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force #=> ["f", "o", "o", "b", "a", "r"]
Ein Wert x, der von block zurückgegeben wird, wird zerlegt, wenn eine der folgenden Bedingungen erfüllt ist:
-
xantwortet sowohl auf each als auch auf force, was bedeutet, dassxein Lazy Enumerator ist. -
xist ein Array oder antwortet auf to_ary.
Andernfalls wird x unverändert in den Rückgabewert aufgenommen.
[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
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);
}
Wie Enumerable#grep, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_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);
}
Wie Enumerable#grep_v, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_map(VALUE obj)
{
LAZY_NEED_BLOCK(map);
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_map_funcs);
}
Wie Enumerable#map, aber verknüpft Operationen zur verzögerten Auswertung.
(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]
Source
static VALUE
lazy_reject(VALUE obj)
{
LAZY_NEED_BLOCK(reject);
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_reject_funcs);
}
Wie Enumerable#reject, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_select(VALUE obj)
{
LAZY_NEED_BLOCK(select);
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_select_funcs);
}
Wie Enumerable#select, aber verknüpft Operationen zur verzögerten Auswertung.
Wie Enumerable#slice_after, aber verknüpft Operationen zur verzögerten Auswertung.
Wie Enumerable#slice_before, aber verknüpft Operationen zur verzögerten Auswertung.
Wie Enumerable#slice_when, aber verknüpft Operationen zur verzögerten Auswertung.
Source
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);
}
Wie Enumerable#take, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_take_while(VALUE obj)
{
LAZY_NEED_BLOCK(take_while);
return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_take_while_funcs);
}
Wie Enumerable#take_while, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_to_a(VALUE self)
{
}
Erweitert den lazy-Enumerator zu einem Array. Siehe Enumerable#to_a.
Source
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;
}
Ähnlich wie Object#to_enum, gibt aber einen Lazy Enumerator zurück. Dies erleichtert die Definition von Enumerable-Methoden, die natürlich faul bleiben, wenn sie von einem Lazy Enumerator aus aufgerufen werden.
Zum Beispiel, fortgesetzt aus dem Beispiel 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]
Source
static VALUE
lazy_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);
}
Wie Enumerable#uniq, aber verknüpft Operationen zur verzögerten Auswertung.
Source
static VALUE
lazy_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);
}
Wenn ein Block gegeben ist, gibt dies einen Lazy Enumerator zurück, der den gegebenen Block für jedes Element mit einem Index durchläuft, beginnend bei offset, und gibt einen Lazy Enumerator zurück, der die gleichen Werte liefert (ohne den Index).
Wenn kein Block gegeben ist, wird ein neuer Lazy Enumerator zurückgegeben, der den Index ab offset enthält.
offset-
der Startindex, der verwendet werden soll
Siehe Enumerator#with_index.
Source
static VALUE
lazy_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);
}
Wie Enumerable#zip, aber verknüpft Operationen zur verzögerten Auswertung. Wenn jedoch ein Block an zip übergeben wird, werden die Werte sofort ausgewertet.
Private Instanzmethoden
Source
static VALUE
enumerator_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));
}
Iteriert den gegebenen Block für jedes Element mit einem Index, beginnend bei offset. Wenn kein Block gegeben ist, wird ein neuer Enumerator zurückgegeben, der den Index ab offset enthält.
offset-
der Startindex, der verwendet werden soll