Spec-Zone.ru › Ruby 3.4

класс Enumerator::Lazy

Родитель:
Enumerator

Enumerator::Lazy — это особый тип Enumerator, который позволяет строить цепочки операций без их немедленного выполнения и оценивать значения по мере необходимости. Для этого он переопределяет большинство методов Enumerable таким образом, что они просто создают другой ленивый перечислитель.

Enumerator::Lazy может быть создан из любого Enumerable с помощью метода Enumerable#lazy.

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>

Фактическое перечисление выполняется при вызове любого непереопределённого метода Enumerable, например, Enumerable#first или Enumerable#to_a (последний алиасируется как force для большей семантической ясности кода):

lazy.first(2)
#=> [21, 23]

lazy.force
#=> [21, 23, 25, 27, 29]

Обратите внимание, что большинство методов Enumerable, которые могут вызываться с блоком или без него, для Enumerator::Lazy всегда требуют блока:

[1, 2, 3].map       #=> #<Enumerator: [1, 2, 3]:map>
[1, 2, 3].lazy.map  # ArgumentError: tried to call lazy map without a block

Этот класс позволяет выполнять идиоматические вычисления на длинных или бесконечных последовательностях, а также объединять вычисления без создания промежуточных массивов.

Пример работы со медленно вычисляемой последовательностью:

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)

Завершение цепочки с помощью «.eager» создаёт не ленивый перечислитель, который подходит для возврата или передачи другому методу, ожидающему обычный перечислитель.

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)

Публичные методы класса

new(obj, size=nil) { |yielder, *values| block }
Исходный код
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;
}

Создаёт новый Lazy перечислитель. Когда перечислитель фактически перечисляется (например, вызовом force), obj будет перечислен, и каждое значение будет передано в заданный блок. Блок может возвращать значения, используя yielder. Например, чтобы создать перечислитель «фильтр+отображение»:

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]

Публичные методы экземпляра

_enumerable_collect ()

Как Enumerable#map, но цепочки операций лениво вычисляются.

(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]
Псевдоним для: collect
_enumerable_collect_concat ()

Возвращает новый ленивый перечислитель с объединенными результатами выполнения block один раз для каждого элемента в ленивом перечислителе.

["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
#=> ["f", "o", "o", "b", "a", "r"]

Значение x возвращаемое block декомпозируется, если выполняется одно из следующих условий:

  • x отвечает на both each and force, что означает, что x является ленивым перечислителем.

  • x является массивом или отвечает на to_ary.

В противном случае, x содержится как есть в возвращаемом значении.

[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
Псевдоним для: collect_concat
_enumerable_drop
Псевдоним для: drop
_enumerable_drop_while
Псевдоним для: drop_while
_enumerable_filter ()

Как Enumerable#select, но цепочки операций лениво вычисляются.

Псевдоним для: filter
_enumerable_filter_map
Псевдоним для: filter_map
_enumerable_find_all ()

Как Enumerable#select, но цепочки операций лениво вычисляются.

Псевдоним для: find_all
_enumerable_flat_map
Псевдоним для: flat_map
_enumerable_grep
Псевдоним для: grep
_enumerable_grep_v
Псевдоним для: grep_v
_enumerable_map
Псевдоним для: map
_enumerable_reject
Псевдоним для: reject
_enumerable_select
Псевдоним для: select
_enumerable_take
Псевдоним для: take
_enumerable_take_while
Псевдоним для: take_while
_enumerable_uniq
Псевдоним для: uniq
_enumerable_zip
Псевдоним для: zip
chunk (*args)
Исходный код
endif

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

Как Enumerable#chunk, но цепочки операций лениво вычисляются.

Также известен как: slice_before, slice_after, slice_when, chunk_while
chunk_while (*args)

Как Enumerable#chunk_while, но цепочки операций лениво вычисляются.

Псевдоним для: chunk
collect { |obj| block } → lazy_enumerator

Как Enumerable#map, но цепочки операций лениво вычисляются.

(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]
Также известен как: _enumerable_collect
Псевдоним для: map
collect_concat { |obj| block } → a_lazy_enumerator

Возвращает новый ленивый перечислитель с объединенными результатами выполнения block один раз для каждого элемента в ленивом перечислителе.

["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
#=> ["f", "o", "o", "b", "a", "r"]

Значение x возвращаемое block декомпозируется, если выполняется одно из следующих условий:

  • x отвечает на both each and force, что означает, что x является ленивым перечислителем.

  • x является массивом или отвечает на to_ary.

В противном случае, x содержится как есть в возвращаемом значении.

[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
Также известен как: _enumerable_collect_concat
Псевдоним для: flat_map
compact → lazy_enumerator
Исходный код
static VALUE
lazy_compact(VALUE obj)
{
    return lazy_add_method(obj, 0, 0, Qnil, Qnil, &lazy_compact_funcs);
}

Как Enumerable#compact, но цепочки операций лениво вычисляются.

drop(n) → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#drop, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_drop
drop_while { |obj| block } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#drop_while, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_drop_while
eager → enum
Исходный код
static VALUE
lazy_eager(VALUE self)
{
    return enumerator_init(enumerator_allocate(rb_cEnumerator),
                           self, sym_each, 0, 0, lazy_eager_size, Qnil, 0);
}

Возвращает неленивый Enumerator, преобразованный из ленивого перечислителя.

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

Аналогично Object#to_enum, но возвращает ленивый перечислитель. Это упрощает определение методов Enumerable, которые останутся ленивыми, если вызваны из ленивого перечислителя.

Например, продолжая пример из 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]
Псевдоним для: to_enum
filter { |obj| block } → lazy_enumerator

Аналогично Enumerable#select, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_filter
Псевдоним для: select
filter_map { |obj| block } → lazy_enumerator
Исходный код
static VALUE
lazy_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);
}

Аналогично Enumerable#filter_map, но цепочечно выполняет операцию для ленивой оценки.

(1..).lazy.filter_map { |i| i * 2 if i.even? }.first(5)
#=> [4, 8, 12, 16, 20]
Также алиасируется как: _enumerable_filter_map
find_all { |obj| block } → lazy_enumerator

Аналогично Enumerable#select, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_find_all
Псевдоним для: select
flat_map { |obj| block } → a_lazy_enumerator
Исходный код
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_add_method(obj, 0, 0, Qnil, Qnil, &lazy_flat_map_funcs);
}

Возвращает новый ленивый перечислитель с конкатенированными результатами выполнения block один раз для каждого элемента в ленивом перечислителе.

["foo", "bar"].lazy.flat_map {|i| i.each_char.lazy}.force
#=> ["f", "o", "o", "b", "a", "r"]

Значение x , возвращаемое block, разлагается, если выполняется хотя бы одно из следующих условий:

  • x отвечает как на each, так и на force, что означает, что x является ленивым перечислителем.

  • x является массивом или отвечает на to_ary.

В противном случае, x включено в возвращаемое значение как есть.

[{a:1}, {b:2}].lazy.flat_map {|i| i}.force
#=> [{:a=>1}, {:b=>2}]
Также алиасируется как: collect_concat, _enumerable_flat_map
force → array

Преобразует ленивый перечислитель в массив. См. Enumerable#to_a.

Псевдоним для: to_a
grep(pattern) → lazy_enumerator
grep(pattern) { |obj| block } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#grep, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_grep
grep_v(pattern) → lazy_enumerator
grep_v(pattern) { |obj| block } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#grep_v, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_grep_v
lazy → lazy_enumerator
Исходный код
static VALUE
lazy_lazy(VALUE obj)
{
    return obj;
}

Возвращает self.

map { |obj| block } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#map, но цепочечно выполняет операцию для ленивой оценки.

(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]
Также алиасируется как: collect, _enumerable_map
reject { |obj| block } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#reject, но цепочечно выполняет операцию для ленивой оценки.

Также алиасируется как: _enumerable_reject
select { |obj| блок } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#select, но цепочкой операций для отложенной оценки.

Также алиасирован как: find_all, filter, _enumerable_select
slice_after (*args)

Аналогично Enumerable#slice_after, но цепочкой операций для отложенной оценки.

Псевдоним для: chunk
slice_before (*args)

Аналогично Enumerable#slice_before, но цепочкой операций для отложенной оценки.

Псевдоним для: chunk
slice_when (*args)

Аналогично Enumerable#slice_when, но цепочкой операций для отложенной оценки.

Псевдоним для: chunk
take(n) → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#take, но цепочкой операций для отложенной оценки.

Также алиасирован как: _enumerable_take
take_while { |obj| блок } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#take_while, но цепочкой операций для отложенной оценки.

Также алиасирован как: _enumerable_take_while
to_a → массив
Исходный код
static VALUE
lazy_to_a(VALUE self)
{
}

Преобразует lazy перечислитель в массив. См. Enumerable#to_a.

Также алиасирован как: force
to_enum(метод = :each, *аргументы) → lazy_enum
to_enum(метод = :each, *аргументы) {|*аргументы| блок } → lazy_enum
Исходный код
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;
}

Аналогично Object#to_enum, но возвращает ленивый перечислитель. Это упрощает определение методов Enumerable, которые естественным образом останутся ленивыми, если вызваны из ленивого перечислителя.

Например, продолжая пример из 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]
Также алиасирован как: enum_for
uniq → lazy_enumerator
uniq { |элемент| блок } → lazy_enumerator
Исходный код
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);
}

Аналогично Enumerable#uniq, но цепочкой операций для отложенной оценки.

Также алиасирован как: _enumerable_uniq
with_index(смещение = 0) {|(*аргументы), индекс| блок }
with_index(смещение = 0)
Исходный код
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);
}

Если задан блок, возвращает ленивый перечислитель, который будет итерировать по заданному блоку для каждого элемента с индексом, начинающимся с offset, и возвращает ленивый перечислитель, который возвращает те же значения (без индекса).

Если блок не задан, возвращает новый ленивый перечислитель, который включает индекс, начинающийся со offset.

offset

начальный индекс для использования

См. Enumerator#with_index.

zip(аргумент, ...) → lazy_enumerator
zip(аргумент, ...) { |массив| блок } → nil
Исходный код
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);
}

Аналогично Enumerable#zip, но цепочкой операций для отложенной оценки. Однако, если для zip задан блок, значения перечисляются немедленно.

Также алиасирован как: _enumerable_zip

Закрытые методы экземпляра

with_index(смещение = 0) {|(*аргументы), индекс| ... }
with_index(смещение = 0)
Исходный код
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));
}

Итерирует заданный блок для каждого элемента с индексом, начиная с offset. Если блок не задан, возвращает новый Enumerator, который включает индекс, начиная с offset.

offset

начальный индекс для использования

Ruby Core © 1993–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API