Spec-Zone.ru › Ruby 2.5

модуль Enumerable

Mixin Enumerable предоставляет классам коллекций несколько методов обхода и поиска, а также возможность сортировки. Класс должен предоставить метод each, который возвращает последовательные члены коллекции. Если используются Enumerable#max, #min, или #sort, объекты в коллекции также должны реализовывать осмысленный оператор <=>, так как эти методы полагаются на упорядочение между членами коллекции.

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

all? [{ |obj| block } ] → true or false Show source
all?(pattern) → true or false
static VALUE
enum_all(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
    return memo->v1;
}

Передает каждый элемент коллекции в заданный блок. Метод возвращает true если блок никогда не возвращает false или nil. Если блок не задан, Ruby добавляет неявный блок { |obj| obj }, который заставит all? возвращать true когда ни один из членов коллекции не является false или nil.

Если вместо этого указан шаблон, метод возвращает, является ли pattern === element для каждого члена коллекции.

%w[ant bear cat].all? { |word| word.length >= 3 } #=> true
%w[ant bear cat].all? { |word| word.length >= 4 } #=> false
%w[ant bear cat].all?(/t/)                        #=> false
[1, 2i, 3.14].all?(Numeric)                       #=> true
[nil, true, 99].all?                              #=> false
[].all?                                           #=> true
any? [{ |obj| block }] → true or false Show source
any?(pattern) → true or false
static VALUE
enum_any(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qfalse);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
    return memo->v1;
}

Передает каждый элемент коллекции в заданный блок. Метод возвращает true если блок когда-либо возвращает значение, отличное от false или nil. Если блок не задан, Ruby добавляет неявный блок { |obj| obj }, который заставит any? возвращать true если хотя бы один из членов коллекции не является false или nil.

Если вместо этого указан шаблон, метод возвращает, является ли pattern === element для любого члена коллекции.

%w[ant bear cat].any? { |word| word.length >= 3 } #=> true
%w[ant bear cat].any? { |word| word.length >= 4 } #=> true
%w[ant bear cat].any?(/d/)                        #=> false
[nil, true, 99].any?(Integer)                     #=> true
[nil, true, 99].any?                              #=> true
[].any?                                           #=> false
chunk { |elt| ... } → an_enumerator Show source
static VALUE
enum_chunk(VALUE enumerable)
{
    VALUE enumerator;

    RETURN_SIZED_ENUMERATOR(enumerable, 0, 0, enum_size);

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, rb_intern("chunk_enumerable"), enumerable);
    rb_ivar_set(enumerator, rb_intern("chunk_categorize"), rb_block_proc());
    rb_block_call(enumerator, idInitialize, 0, 0, chunk_i, enumerator);
    return enumerator;
}

Перечисляет элементы, группируя их вместе на основе возвращаемого значения блока.

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

Например, последовательные четные и нечетные числа можно сгруппировать следующим образом.

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5].chunk { |n|
  n.even?
}.each { |even, ary|
  p [even, ary]
}
#=> [false, [3, 1]]
#   [true, [4]]
#   [false, [1, 5, 9]]
#   [true, [2, 6]]
#   [false, [5, 3, 5]]

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

open("/usr/share/dict/words", "r:iso-8859-1") { |f|
  f.chunk { |line| line.ord }.each { |ch, lines| p [ch.chr, lines.length] }
}
#=> ["\n", 1]
#   ["A", 1327]
#   ["B", 1372]
#   ["C", 1507]
#   ["D", 791]
#   ...

Следующие ключевые значения имеют специальное значение:

  • nil и :_separator указывают, что элементы должны быть отброшены.

  • :_alone указывает, что элемент должен быть сгруппирован сам по себе.

Любые другие символы, начинающиеся с подчеркивания, вызовут ошибку:

items.chunk { |item| :_underscore }
#=> RuntimeError: symbols beginning with an underscore are reserved

nil и :_separator можно использовать для игнорирования некоторых элементов.

Например, последовательность дефисов в журнале svn можно исключить следующим образом:

sep = "-"*72 + "\n"
IO.popen("svn log README") { |f|
  f.chunk { |line|
    line != sep || nil
  }.each { |_, lines|
    pp lines
  }
}
#=> ["r20018 | knu | 2008-10-29 13:20:42 +0900 (Wed, 29 Oct 2008) | 2 lines\n",
#    "\n",
#    "* README, README.ja: Update the portability section.\n",
#    "\n"]
#   ["r16725 | knu | 2008-05-31 23:34:23 +0900 (Sat, 31 May 2008) | 2 lines\n",
#    "\n",
#    "* README, README.ja: Add a note about default C flags.\n",
#    "\n"]
#   ...

Абзацы, разделенные пустыми строками, можно разобрать следующим образом:

File.foreach("README").chunk { |line|
  /\A\s*\z/ !~ line || nil
}.each { |_, lines|
  pp lines
}

:_alone можно использовать для принудительного объединения элементов в свою собственную группу. Например, можно поместить строки, содержащие URL-адрес, сами по себе, а остальные строки сгруппировать вместе, например так:

pattern = /http/
open(filename) { |f|
  f.chunk { |line| line =~ pattern ? :_alone : true }.each { |key, lines|
    pp lines
  }
}

Если блок не задан, вместо этого возвращается перечислитель для `chunk`.

chunk_while {|elt_before, elt_after| bool } → an_enumerator Show source
static VALUE
enum_chunk_while(VALUE enumerable)
{
    VALUE enumerator;
    VALUE pred;

    pred = rb_block_proc();

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, rb_intern("slicewhen_enum"), enumerable);
    rb_ivar_set(enumerator, rb_intern("slicewhen_pred"), pred);
    rb_ivar_set(enumerator, rb_intern("slicewhen_inverted"), Qtrue);

    rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
    return enumerator;
}

Создает перечислитель для каждого сгруппированного элемента. Начала групп определяются блоком.

Этот метод разделяет каждую группу, используя смежные элементы, elt_before и elt_after, в перечислителе получателя. Этот метод разделяет группы между elt_before и elt_after, где блок возвращает false.

Блок вызывается длиной перечислителя получателя минус один.

Результирующий перечислитель дает сгруппированные элементы в виде массива. Таким образом, метод each может быть вызван следующим образом:

enum.chunk_while { |elt_before, elt_after| bool }.each { |ary| ... }

Другие методы класса Enumerator и модуля Enumerable, такие как to_a, map и т. д., также пригодны для использования.

Например, подпоследовательность, возрастающая по одному, может быть сгруппирована следующим образом:

a = [1,2,4,9,10,11,12,15,16,19,20,21]
b = a.chunk_while {|i, j| i+1 == j }
p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" }
p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"]
d = c.join(",")
p d #=> "1,2,4,9-12,15,16,19-21"

Возрастающая (неубывающая) подпоследовательность может быть сгруппирована следующим образом:

a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5]
p a.chunk_while {|i, j| i <= j }.to_a
#=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]]

Смежные четные и нечетные числа можно сгруппировать следующим образом: (Enumerable#chunk — это еще один способ сделать это).

a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0]
p a.chunk_while {|i, j| i.even? == j.even? }.to_a
#=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]]

#slice_when делает то же самое, за исключением разделения, когда блок возвращает true вместо false.

collect { |obj| block } → array Show source
collect → an_enumerator
static VALUE
enum_collect(VALUE obj)
{
    VALUE ary;
    int min_argc, max_argc;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    min_argc = rb_block_min_max_arity(&max_argc);
    rb_lambda_call(obj, id_each, 0, 0, collect_i, min_argc, max_argc, ary);

    return ary;
}

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

Если блок не задан, вместо этого возвращается перечислитель.

(1..4).map { |i| i*i }      #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
collect_concat { |obj| block } → array Show source
collect_concat → an_enumerator
static VALUE
enum_flat_map(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);

    return ary;
}

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

Если блок не задан, вместо этого возвращается перечислитель.

[1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
[[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
count → int Show source
count(item) → int
count { |obj| block } → int
static VALUE
enum_count(int argc, VALUE *argv, VALUE obj)
{
    VALUE item = Qnil;
    struct MEMO *memo;
    rb_block_call_func *func;

    if (argc == 0) {
        if (rb_block_given_p()) {
            func = count_iter_i;
        }
        else {
            func = count_all_i;
        }
    }
    else {
        rb_scan_args(argc, argv, "1", &item);
        if (rb_block_given_p()) {
            rb_warn("given block not used");
        }
        func = count_i;
    }

    memo = MEMO_NEW(item, 0, 0);
    rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
    return imemo_count_value(memo);
}

Возвращает количество элементов в enum с помощью перечисления. Если задан аргумент, подсчитывается количество элементов в enum, которые равны item. Если задан блок, он подсчитывает количество элементов, дающих истинное значение.

ary = [1, 2, 4, 2]
ary.count               #=> 4
ary.count(2)            #=> 2
ary.count{ |x| x%2==0 } #=> 3
cycle(n=nil) { |obj| block } → nil Show source
cycle(n=nil) → an_enumerator
static VALUE
enum_cycle(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary;
    VALUE nv = Qnil;
    long n, i, len;

    rb_scan_args(argc, argv, "01", &nv);

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size);
    if (NIL_P(nv)) {
        n = -1;
    }
    else {
        n = NUM2LONG(nv);
        if (n <= 0) return Qnil;
    }
    ary = rb_ary_new();
    RBASIC_CLEAR_CLASS(ary);
    rb_block_call(obj, id_each, 0, 0, cycle_i, ary);
    len = RARRAY_LEN(ary);
    if (len == 0) return Qnil;
    while (n < 0 || 0 < --n) {
        for (i=0; i<len; i++) {
            enum_yield_array(RARRAY_AREF(ary, i));
        }
    }
    return Qnil;
}

Вызывает block для каждого элемента enum многократно n раз или бесконечно, если не задано ни одного или nil . Если задано неположительное число или коллекция пуста, ничего не делает. Возвращает nil, если цикл завершился без прерывания.

#cycle сохраняет элементы во внутреннем массиве, поэтому изменения в enum после первого прохода не оказывают никакого влияния.

Если блок не задан, вместо этого возвращается перечислитель.

a = ["a", "b", "c"]
a.cycle { |x| puts x }  # print, a, b, c, a, b, c,.. forever.
a.cycle(2) { |x| puts x }  # print, a, b, c, a, b, c.
detect(ifnone = nil) { |obj| block } → obj or nil Show source
detect(ifnone = nil) → an_enumerator
static VALUE
enum_find(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE if_none;

    rb_scan_args(argc, argv, "01", &if_none);
    RETURN_ENUMERATOR(obj, argc, argv);
    memo = MEMO_NEW(Qundef, 0, 0);
    rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
    if (memo->u3.cnt) {
        return memo->v1;
    }
    if (!NIL_P(if_none)) {
        return rb_funcallv(if_none, id_call, 0, 0);
    }
    return Qnil;
}

Передает каждую запись в enum в block. Возвращает первую, для которой block не ложен. Если ни один объект не соответствует, вызывает ifnone и возвращает его результат, если он указан, или возвращает nil в противном случае.

Если блок не задан, вместо этого возвращается перечислитель.

(1..100).detect  => #<Enumerator: 1..100:detect>
(1..100).find    => #<Enumerator: 1..100:find>

(1..10).detect   { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
(1..10).find     { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
(1..100).detect  { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35
(1..100).find    { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35
drop(n) → array Show source
static VALUE
enum_drop(VALUE obj, VALUE n)
{
    VALUE result;
    struct MEMO *memo;
    long len = NUM2LONG(n);

    if (len < 0) {
        rb_raise(rb_eArgError, "attempt to drop negative size");
    }

    result = rb_ary_new();
    memo = MEMO_NEW(result, 0, len);
    rb_block_call(obj, id_each, 0, 0, drop_i, (VALUE)memo);
    return result;
}

Удаляет первые n элементов из enum и возвращает остальные элементы в массиве.

a = [1, 2, 3, 4, 5, 0]
a.drop(3)             #=> [4, 5, 0]
drop_while { |obj| block } → array Show source
drop_while → an_enumerator
static VALUE
enum_drop_while(VALUE obj)
{
    VALUE result;
    struct MEMO *memo;

    RETURN_ENUMERATOR(obj, 0, 0);
    result = rb_ary_new();
    memo = MEMO_NEW(result, 0, FALSE);
    rb_block_call(obj, id_each, 0, 0, drop_while_i, (VALUE)memo);
    return result;
}

Удаляет элементы вплоть до, но не включая, первого элемента, для которого блок возвращает nil или false, и возвращает массив, содержащий оставшиеся элементы.

Если блок не задан, вместо этого возвращается перечислитель.

a = [1, 2, 3, 4, 5, 0]
a.drop_while { |i| i < 3 }   #=> [3, 4, 5, 0]
each_cons(n) { ... } → nil Show source
each_cons(n) → an_enumerator
static VALUE
enum_each_cons(VALUE obj, VALUE n)
{
    long size = NUM2LONG(n);
    struct MEMO *memo;
    int arity;

    if (size <= 0) rb_raise(rb_eArgError, "invalid size");
    RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_cons_size);
    arity = rb_block_arity();
    if (enum_size_over_p(obj, size)) return Qnil;
    memo = MEMO_NEW(rb_ary_new2(size), dont_recycle_block_arg(arity), size);
    rb_block_call(obj, id_each, 0, 0, each_cons_i, (VALUE)memo);

    return Qnil;
}

Итерирует заданный блок для каждого массива из последовательных <n> элементов. Если блок не задан, возвращает перечислитель.

Например:

(1..10).each_cons(3) { |a| p a }
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
each_entry { |obj| block } → enum Показать исходный код
each_entry → an_enumerator
static VALUE
enum_each_entry(int argc, VALUE *argv, VALUE obj)
{
    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);
    rb_block_call(obj, id_each, argc, argv, each_val_i, 0);
    return obj;
}

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

Если блок не указан, вместо него возвращается перечислитель.

class Foo
  include Enumerable
  def each
    yield 1
    yield 1, 2
    yield
  end
end
Foo.new.each_entry{ |o| p o }

выводит:

1
[1, 2]
nil
each_slice(n) { ... } → nil Показать исходный код
each_slice(n) → an_enumerator
static VALUE
enum_each_slice(VALUE obj, VALUE n)
{
    long size = NUM2LONG(n);
    VALUE ary;
    struct MEMO *memo;
    int arity;

    if (size <= 0) rb_raise(rb_eArgError, "invalid slice size");
    RETURN_SIZED_ENUMERATOR(obj, 1, &n, enum_each_slice_size);
    size = limit_by_enum_size(obj, size);
    ary = rb_ary_new2(size);
    arity = rb_block_arity();
    memo = MEMO_NEW(ary, dont_recycle_block_arg(arity), size);
    rb_block_call(obj, id_each, 0, 0, each_slice_i, (VALUE)memo);
    ary = memo->v1;
    if (RARRAY_LEN(ary) > 0) rb_yield(ary);

    return Qnil;
}

Итерирует заданный блок для каждого фрагмента из <n> элементов. Если блок не указан, возвращает перечислитель.

(1..10).each_slice(3) { |a| p a }
# outputs below
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
each_with_index(*args) { |obj, i| block } → enum Показать исходный код
each_with_index(*args) → an_enumerator
static VALUE
enum_each_with_index(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    memo = MEMO_NEW(0, 0, 0);
    rb_block_call(obj, id_each, argc, argv, each_with_index_i, (VALUE)memo);
    return obj;
}

Вызывает block с двумя аргументами, элементом и его индексом, для каждого элемента в enum. Переданные аргументы передаются в each().

Если блок не указан, возвращается перечислитель вместо него.

hash = Hash.new
%w(cat dog wombat).each_with_index { |item, index|
  hash[item] = index
}
hash   #=> {"cat"=>0, "dog"=>1, "wombat"=>2}
each_with_object(obj) { |(*args), memo_obj| ... } → obj Показать исходный код
each_with_object(obj) → an_enumerator
static VALUE
enum_each_with_object(VALUE obj, VALUE memo)
{
    RETURN_SIZED_ENUMERATOR(obj, 1, &memo, enum_size);

    rb_block_call(obj, id_each, 0, 0, each_with_object_i, memo);

    return memo;
}

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

Если блок не указан, возвращается перечислитель.

evens = (1..10).each_with_object([]) { |i, a| a << i*2 }
#=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
entries(*args) → array Показать исходный код
static VALUE
enum_to_a(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary = rb_ary_new();

    rb_block_call(obj, id_each, argc, argv, collect_all, ary);
    OBJ_INFECT(ary, obj);

    return ary;
}

Возвращает массив, содержащий элементы в enum.

(1..7).to_a                       #=> [1, 2, 3, 4, 5, 6, 7]
{ 'a'=>1, 'b'=>2, 'c'=>3 }.to_a   #=> [["a", 1], ["b", 2], ["c", 3]]

require 'prime'
Prime.entries 10                  #=> [2, 3, 5, 7]
find(ifnone = nil) { |obj| block } → obj or nil Показать исходный код
find(ifnone = nil) → an_enumerator
static VALUE
enum_find(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE if_none;

    rb_scan_args(argc, argv, "01", &if_none);
    RETURN_ENUMERATOR(obj, argc, argv);
    memo = MEMO_NEW(Qundef, 0, 0);
    rb_block_call(obj, id_each, 0, 0, find_i, (VALUE)memo);
    if (memo->u3.cnt) {
        return memo->v1;
    }
    if (!NIL_P(if_none)) {
        return rb_funcallv(if_none, id_call, 0, 0);
    }
    return Qnil;
}

Передает каждый элемент в enum в block. Возвращает первый элемент, для которого block не равен false. Если ни один объект не соответствует условию, вызывается ifnone и возвращает его результат, если он указан, иначе возвращает nil.

Если блок не указан, возвращается перечислитель.

(1..100).detect  => #<Enumerator: 1..100:detect>
(1..100).find    => #<Enumerator: 1..100:find>

(1..10).detect   { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
(1..10).find     { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
(1..100).detect  { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35
(1..100).find    { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35
find_all { |obj| block } → array Показать исходный код
find_all → an_enumerator
static VALUE
enum_find_all(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, find_all_i, ary);

    return ary;
}

Возвращает массив, содержащий все элементы enum, для которых заданный block возвращает значение true.

Если блок не указан, вместо него возвращается Enumerator.

(1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]

[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

См. также #reject.

find_index(value) → int or nil Показать исходный код
find_index { |obj| block } → int or nil
find_index → an_enumerator
static VALUE
enum_find_index(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;  /* [return value, current index, ] */
    VALUE condition_value = Qnil;
    rb_block_call_func *func;

    if (argc == 0) {
        RETURN_ENUMERATOR(obj, 0, 0);
        func = find_index_iter_i;
    }
    else {
        rb_scan_args(argc, argv, "1", &condition_value);
        if (rb_block_given_p()) {
            rb_warn("given block not used");
        }
        func = find_index_i;
    }

    memo = MEMO_NEW(Qnil, condition_value, 0);
    rb_block_call(obj, id_each, 0, 0, func, (VALUE)memo);
    return memo->v1;
}

Сравнивает каждый элемент в enum со значением value или передает его в block. Возвращает индекс первого элемента, для которого вычисленное значение не равно false. Если ни один объект не соответствует, возвращает nil

Если ни блок, ни аргумент не заданы, возвращается перечислитель.

(1..10).find_index  { |i| i % 5 == 0 and i % 7 == 0 }  #=> nil
(1..100).find_index { |i| i % 5 == 0 and i % 7 == 0 }  #=> 34
(1..100).find_index(50)                                #=> 49
first → obj or nil Показать исходный код
first(n) → an_array
static VALUE
enum_first(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    rb_check_arity(argc, 0, 1);
    if (argc > 0) {
        return enum_take(obj, argv[0]);
    }
    else {
        memo = MEMO_NEW(Qnil, 0, 0);
        rb_block_call(obj, id_each, 0, 0, first_i, (VALUE)memo);
        return memo->v1;
    }
}

Возвращает первый элемент или первые n элементы перечислимого объекта. Если перечислимый объект пустой, первая форма возвращает nil, а вторая форма возвращает пустой массив.

%w[foo bar baz].first     #=> "foo"
%w[foo bar baz].first(2)  #=> ["foo", "bar"]
%w[foo bar baz].first(10) #=> ["foo", "bar", "baz"]
[].first                  #=> nil
[].first(10)              #=> []
flat_map { |obj| block } → array Показать исходный код
flat_map → an_enumerator
static VALUE
enum_flat_map(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, flat_map_i, ary);

    return ary;
}

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

Если блок не указан, возвращается перечислитель.

[1, 2, 3, 4].flat_map { |e| [e, -e] } #=> [1, -1, 2, -2, 3, -3, 4, -4]
[[1, 2], [3, 4]].flat_map { |e| e + [100] } #=> [1, 2, 100, 3, 4, 100]
grep(pattern) → array Показать исходный код
grep(pattern) { |obj| block } → array
static VALUE
enum_grep(VALUE obj, VALUE pat)
{
    VALUE ary = rb_ary_new();
    struct MEMO *memo = MEMO_NEW(pat, ary, Qtrue);

    rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)memo);

    return ary;
}

Возвращает массив всех элементов в enum, для которых Pattern === element. Если опциональный block указан, каждый совпадающий элемент передаётся ему, и результат блока сохраняется в выходном массиве.

(1..100).grep 38..44   #=> [38, 39, 40, 41, 42, 43, 44]
c = IO.constants
c.grep(/SEEK/)         #=> [:SEEK_SET, :SEEK_CUR, :SEEK_END]
res = c.grep(/SEEK/) { |v| IO.const_get(v) }
res                    #=> [0, 1, 2]
grep_v(pattern) → array Показать исходный код
grep_v(pattern) { |obj| block } → array
static VALUE
enum_grep_v(VALUE obj, VALUE pat)
{
    VALUE ary = rb_ary_new();
    struct MEMO *memo = MEMO_NEW(pat, ary, Qfalse);

    rb_block_call(obj, id_each, 0, 0, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)memo);

    return ary;
}

Обратная версия #grep. Возвращает массив всех элементов в enum, для которых не Pattern === element.

(1..10).grep_v 2..5   #=> [1, 6, 7, 8, 9, 10]
res =(1..10).grep_v(2..5) { |v| v * 2 }
res                    #=> [2, 12, 14, 16, 18, 20]
group_by { |obj| block } → a_hash Показать исходный код
group_by → an_enumerator
static VALUE
enum_group_by(VALUE obj)
{
    VALUE hash;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    hash = rb_hash_new();
    rb_block_call(obj, id_each, 0, 0, group_by_i, hash);
    OBJ_INFECT(hash, obj);

    return hash;
}

Группирует коллекцию по результату блока. Возвращает словарь, где ключами являются значения, вычисленные из блока, а значениями — массивы элементов в коллекции, соответствующие ключу.

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

(1..6).group_by { |i| i%3 }   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
include?(obj) → true or false Показать исходный код
static VALUE
enum_member(VALUE obj, VALUE val)
{
    struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

    rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
    return memo->v2;
}

Возвращает true, если любой элемент в enum равен obj. Равенство проверяется с использованием ==.

IO.constants.include? :SEEK_SET          #=> true
IO.constants.include? :SEEK_NO_FURTHER   #=> false
IO.constants.member? :SEEK_SET          #=> true
IO.constants.member? :SEEK_NO_FURTHER   #=> false
inject(initial, sym) → obj Показать исходный код
inject(sym) → obj
inject(initial) { |memo, obj| block } → obj
inject { |memo, obj| block } → obj
static VALUE
enum_inject(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE init, op;
    rb_block_call_func *iter = inject_i;
    ID id;

    switch (rb_scan_args(argc, argv, "02", &init, &op)) {
      case 0:
        init = Qundef;
        break;
      case 1:
        if (rb_block_given_p()) {
            break;
        }
        id = rb_check_id(&init);
        op = id ? ID2SYM(id) : init;
        init = Qundef;
        iter = inject_op_i;
        break;
      case 2:
        if (rb_block_given_p()) {
            rb_warning("given block not used");
        }
        id = rb_check_id(&op);
        if (id) op = ID2SYM(id);
        iter = inject_op_i;
        break;
    }

    if (iter == inject_op_i &&
        SYMBOL_P(op) &&
        RB_TYPE_P(obj, T_ARRAY) &&
        rb_method_basic_definition_p(CLASS_OF(obj), id_each)) {
        return ary_inject_op(obj, init, op);
    }

    memo = MEMO_NEW(init, Qnil, op);
    rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
    if (memo->v1 == Qundef) return Qnil;
    return memo->v1;
}

Объединяет все элементы enum, применяя бинарную операцию, указанную блоком или символом, обозначающим метод или оператор.

Методы inject и reduce являются псевдонимами. Разницы в производительности нет.

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

Если вы не указываете явное начальное значение для memo, то первый элемент коллекции используется как начальное значение memo.

# Sum some numbers
(5..10).reduce(:+)                             #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n }            #=> 45
# Multiply some numbers
(5..10).reduce(1, :*)                          #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
   memo.length > word.length ? memo : word
end
longest                                        #=> "sheep"
lazy → lazy_enumerator Show source
static VALUE
enumerable_lazy(VALUE obj)
{
    VALUE result = lazy_to_enum_i(obj, sym_each, 0, 0, lazyenum_size);
    /* Qfalse indicates that the Enumerator::Lazy has no method name */
    rb_ivar_set(result, id_method, Qfalse);
    return result;
}

Возвращает ленивый перечислитель, чьи методы map/collect, flat_map/collect_concat, select/find_all, reject, grep, #grep_v, zip, take, #take_while, drop и #drop_while перечисляют значения только по мере необходимости. Однако, если для zip задан блок, значения перечисляются немедленно.

Пример

Следующая программа находит пифагоровы тройки:

def pythagorean_triples
  (1..Float::INFINITY).lazy.flat_map {|z|
    (1..z).flat_map {|x|
      (x..z).select {|y|
        x**2 + y**2 == z**2
      }.map {|y|
        [x, y, z]
      }
    }
  }
end
# show first ten pythagorean triples
p pythagorean_triples.take(10).force # take is lazy, so force is needed
p pythagorean_triples.first(10)      # first is eager
# show pythagorean triples less than 100
p pythagorean_triples.take_while { |*, z| z < 100 }.force
map { |obj| block } → array Show source
map → an_enumerator
static VALUE
enum_collect(VALUE obj)
{
    VALUE ary;
    int min_argc, max_argc;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    min_argc = rb_block_min_max_arity(&max_argc);
    rb_lambda_call(obj, id_each, 0, 0, collect_i, min_argc, max_argc, ary);

    return ary;
}

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

Если блок не задан, вместо этого возвращается перечислитель.

(1..4).map { |i| i*i }      #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
max → obj Show source
max { |a, b| block } → obj
max(n) → array
max(n) { |a, b| block } → array
static VALUE
enum_max(int argc, VALUE *argv, VALUE obj)
{
    VALUE memo;
    struct max_t *m = NEW_CMP_OPT_MEMO(struct max_t, memo);
    VALUE result;
    VALUE num;

    rb_scan_args(argc, argv, "01", &num);

    if (!NIL_P(num))
       return rb_nmin_run(obj, num, 0, 1, 0);

    m->max = Qundef;
    m->cmp_opt.opt_methods = 0;
    m->cmp_opt.opt_inited = 0;
    if (rb_block_given_p()) {
        rb_block_call(obj, id_each, 0, 0, max_ii, (VALUE)memo);
    }
    else {
        rb_block_call(obj, id_each, 0, 0, max_i, (VALUE)memo);
    }
    result = m->max;
    if (result == Qundef) return Qnil;
    return result;
}

Возвращает объект в enum с максимальным значением. Первая форма предполагает, что все объекты реализуют Comparable; вторая использует блок для возврата a <=> b.

a = %w(albatross dog horse)
a.max                                   #=> "horse"
a.max { |a, b| a.length <=> b.length }  #=> "albatross"

Если задан аргумент n, возвращаются максимальные n элементов в виде отсортированного по убыванию массива.

a = %w[albatross dog horse]
a.max(2)                                  #=> ["horse", "dog"]
a.max(2) {|a, b| a.length <=> b.length }  #=> ["albatross", "horse"]
[5, 1, 3, 4, 2].max(3)                    #=> [5, 4, 3]
max_by {|obj| block } → obj Show source
max_by → an_enumerator
max_by(n) {|obj| block } → obj
max_by(n) → an_enumerator
static VALUE
enum_max_by(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE num;

    rb_scan_args(argc, argv, "01", &num);

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    if (!NIL_P(num))
        return rb_nmin_run(obj, num, 1, 1, 0);

    memo = MEMO_NEW(Qundef, Qnil, 0);
    rb_block_call(obj, id_each, 0, 0, max_by_i, (VALUE)memo);
    return memo->v2;
}

Возвращает объект в enum, который дает максимальное значение из заданного блока.

Если блок не задан, вместо этого возвращается перечислитель.

a = %w(albatross dog horse)
a.max_by { |x| x.length }   #=> "albatross"

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

a = %w[albatross dog horse]
a.max_by(2) {|x| x.length } #=> ["albatross", "horse"]

enum.max_by(n) может использоваться для реализации взвешенной случайной выборки. Следующий пример реализует и использует Enumerable#wsample.

module Enumerable
  # weighted random sampling.
  #
  # Pavlos S. Efraimidis, Paul G. Spirakis
  # Weighted random sampling with a reservoir
  # Information Processing Letters
  # Volume 97, Issue 5 (16 March 2006)
  def wsample(n)
    self.max_by(n) {|v| rand ** (1.0/yield(v)) }
  end
end
e = (-20..20).to_a*10000
a = e.wsample(20000) {|x|
  Math.exp(-(x/5.0)**2) # normal distribution
}
# a is 20000 samples from e.
p a.length #=> 20000
h = a.group_by {|x| x }
-10.upto(10) {|x| puts "*" * (h[x].length/30.0).to_i if h[x] }
#=> *
#   ***
#   ******
#   ***********
#   ******************
#   *****************************
#   *****************************************
#   ****************************************************
#   ***************************************************************
#   ********************************************************************
#   ***********************************************************************
#   ***********************************************************************
#   **************************************************************
#   ****************************************************
#   ***************************************
#   ***************************
#   ******************
#   ***********
#   *******
#   ***
#   *
member?(obj) → true or false Show source
static VALUE
enum_member(VALUE obj, VALUE val)
{
    struct MEMO *memo = MEMO_NEW(val, Qfalse, 0);

    rb_block_call(obj, id_each, 0, 0, member_i, (VALUE)memo);
    return memo->v2;
}

Возвращает true, если какой-либо член enum равен obj. Равенство проверяется с использованием ==.

IO.constants.include? :SEEK_SET          #=> true
IO.constants.include? :SEEK_NO_FURTHER   #=> false
IO.constants.member? :SEEK_SET          #=> true
IO.constants.member? :SEEK_NO_FURTHER   #=> false
min → obj Show source
min { |a, b| block } → obj
min(n) → array
min(n) { |a, b| block } → array
static VALUE
enum_min(int argc, VALUE *argv, VALUE obj)
{
    VALUE memo;
    struct min_t *m = NEW_CMP_OPT_MEMO(struct min_t, memo);
    VALUE result;
    VALUE num;

    rb_scan_args(argc, argv, "01", &num);

    if (!NIL_P(num))
       return rb_nmin_run(obj, num, 0, 0, 0);

    m->min = Qundef;
    m->cmp_opt.opt_methods = 0;
    m->cmp_opt.opt_inited = 0;
    if (rb_block_given_p()) {
        rb_block_call(obj, id_each, 0, 0, min_ii, memo);
    }
    else {
        rb_block_call(obj, id_each, 0, 0, min_i, memo);
    }
    result = m->min;
    if (result == Qundef) return Qnil;
    return result;
}

Возвращает объект в enum с минимальным значением. Первая форма предполагает, что все объекты реализуют Comparable; вторая использует блок для возврата a <=> b.

a = %w(albatross dog horse)
a.min                                   #=> "albatross"
a.min { |a, b| a.length <=> b.length }  #=> "dog"

Если задан аргумент n, возвращаются минимальные n элементов в виде отсортированного массива.

a = %w[albatross dog horse]
a.min(2)                                  #=> ["albatross", "dog"]
a.min(2) {|a, b| a.length <=> b.length }  #=> ["dog", "horse"]
[5, 1, 3, 4, 2].min(3)                    #=> [1, 2, 3]
min_by {|obj| block } → obj Show source
min_by → an_enumerator
min_by(n) {|obj| block } → array
min_by(n) → an_enumerator
static VALUE
enum_min_by(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE num;

    rb_scan_args(argc, argv, "01", &num);

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    if (!NIL_P(num))
        return rb_nmin_run(obj, num, 1, 0, 0);

    memo = MEMO_NEW(Qundef, Qnil, 0);
    rb_block_call(obj, id_each, 0, 0, min_by_i, (VALUE)memo);
    return memo->v2;
}

Возвращает объект в enum, который дает минимальное значение из заданного блока.

Если блок не задан, вместо этого возвращается перечислитель.

a = %w(albatross dog horse)
a.min_by { |x| x.length }   #=> "dog"

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

a = %w[albatross dog horse]
p a.min_by(2) {|x| x.length } #=> ["dog", "horse"]
minmax → [min, max] Show source
minmax { |a, b| block } → [min, max]
static VALUE
enum_minmax(VALUE obj)
{
    VALUE memo;
    struct minmax_t *m = NEW_CMP_OPT_MEMO(struct minmax_t, memo);

    m->min = Qundef;
    m->last = Qundef;
    m->cmp_opt.opt_methods = 0;
    m->cmp_opt.opt_inited = 0;
    if (rb_block_given_p()) {
        rb_block_call(obj, id_each, 0, 0, minmax_ii, memo);
        if (m->last != Qundef)
            minmax_ii_update(m->last, m->last, m);
    }
    else {
        rb_block_call(obj, id_each, 0, 0, minmax_i, memo);
        if (m->last != Qundef)
            minmax_i_update(m->last, m->last, m);
    }
    if (m->min != Qundef) {
        return rb_assoc_new(m->min, m->max);
    }
    return rb_assoc_new(Qnil, Qnil);
}

Возвращает двухэлементный массив, содержащий минимальное и максимальное значение в перечислимом объекте. Первая форма предполагает, что все объекты реализуют Comparable; вторая использует блок для возврата a <=> b.

a = %w(albatross dog horse)
a.minmax                                  #=> ["albatross", "horse"]
a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"]
minmax_by { |obj| block } → [min, max] Show source
minmax_by → an_enumerator
static VALUE
enum_minmax_by(VALUE obj)
{
    VALUE memo;
    struct minmax_by_t *m = NEW_MEMO_FOR(struct minmax_by_t, memo);

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    m->min_bv = Qundef;
    m->max_bv = Qundef;
    m->min = Qnil;
    m->max = Qnil;
    m->last_bv = Qundef;
    m->last = Qundef;
    rb_block_call(obj, id_each, 0, 0, minmax_by_i, memo);
    if (m->last_bv != Qundef)
        minmax_by_i_update(m->last_bv, m->last_bv, m->last, m->last, m);
    m = MEMO_FOR(struct minmax_by_t, memo);
    return rb_assoc_new(m->min, m->max);
}

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

Если блок не задан, вместо этого возвращается перечислитель.

a = %w(albatross dog horse)
a.minmax_by { |x| x.length }   #=> ["dog", "albatross"]
none? [{ |obj| block }] → true or false Show source
none?(pattern) → true or false
static VALUE
enum_none(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
    return memo->v1;
}

Передает каждый элемент коллекции в заданный блок. Метод возвращает true, если блок никогда не возвращает true для всех элементов. Если блок не задан, none? вернет true, только если ни один из членов коллекции не является истинным.

Если вместо этого задан шаблон, метод возвращает, соответствует ли pattern === element ни одному из членов коллекции.

%w{ant bear cat}.none? { |word| word.length == 5 } #=> true
%w{ant bear cat}.none? { |word| word.length >= 4 } #=> false
%w{ant bear cat}.none?(/d/)                        #=> true
[1, 3.14, 42].none?(Float)                         #=> false
[].none?                                           #=> true
[nil].none?                                        #=> true
[nil, false].none?                                 #=> true
[nil, false, true].none?                           #=> false
one? [{ |obj| block }] → true or false Show source
one?(pattern) → true or false
static VALUE
enum_one(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qundef);
    VALUE result;

    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo);
    result = memo->v1;
    if (result == Qundef) return Qfalse;
    return result;
}

Передает каждый элемент коллекции в заданный блок. Метод возвращает true, если блок возвращает true ровно один раз. Если блок не задан, one? вернет true, только если ровно один из членов коллекции является истинным.

Если вместо этого задан шаблон, метод возвращает, соответствует ли pattern === element ровно одному члену коллекции.

%w{ant bear cat}.one? { |word| word.length == 4 }  #=> true
%w{ant bear cat}.one? { |word| word.length > 4 }   #=> false
%w{ant bear cat}.one? { |word| word.length < 4 }   #=> false
%w{ant bear cat}.one?(/t/)                         #=> false
[ nil, true, 99 ].one?                             #=> false
[ nil, true, false ].one?                          #=> true
[ nil, true, 99 ].one?(Integer)                    #=> true
[].one?                                            #=> false
partition { |obj| block } → [ true_array, false_array ] Show source
partition → an_enumerator
static VALUE
enum_partition(VALUE obj)
{
    struct MEMO *memo;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    memo = MEMO_NEW(rb_ary_new(), rb_ary_new(), 0);
    rb_block_call(obj, id_each, 0, 0, partition_i, (VALUE)memo);

    return rb_assoc_new(memo->v1, memo->v2);
}

Возвращает два массива: первый содержит элементы enum, для которых блок вычисляет значение true, второй содержит остальные.

Если блок не задан, вместо этого возвращается перечислитель.

(1..6).partition { |v| v.even? }  #=> [[2, 4, 6], [1, 3, 5]]
reduce(initial, sym) → obj Show source
reduce(sym) → obj
reduce(initial) { |memo, obj| block } → obj
reduce { |memo, obj| block } → obj
static VALUE
enum_inject(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo;
    VALUE init, op;
    rb_block_call_func *iter = inject_i;
    ID id;

    switch (rb_scan_args(argc, argv, "02", &init, &op)) {
      case 0:
        init = Qundef;
        break;
      case 1:
        if (rb_block_given_p()) {
            break;
        }
        id = rb_check_id(&init);
        op = id ? ID2SYM(id) : init;
        init = Qundef;
        iter = inject_op_i;
        break;
      case 2:
        if (rb_block_given_p()) {
            rb_warning("given block not used");
        }
        id = rb_check_id(&op);
        if (id) op = ID2SYM(id);
        iter = inject_op_i;
        break;
    }

    if (iter == inject_op_i &&
        SYMBOL_P(op) &&
        RB_TYPE_P(obj, T_ARRAY) &&
        rb_method_basic_definition_p(CLASS_OF(obj), id_each)) {
        return ary_inject_op(obj, init, op);
    }

    memo = MEMO_NEW(init, Qnil, op);
    rb_block_call(obj, id_each, 0, 0, iter, (VALUE)memo);
    if (memo->v1 == Qundef) return Qnil;
    return memo->v1;
}

Объединяет все элементы enum, применяя бинарную операцию, заданную блоком или символом, который обозначает метод или оператор.

Методы inject и reduce являются псевдонимами. Разницы в производительности нет.

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

Если вы не указываете явно начальное значение для memo, то первый элемент коллекции используется в качестве начального значения для memo.

# Sum some numbers
(5..10).reduce(:+)                             #=> 45
# Same using a block and inject
(5..10).inject { |sum, n| sum + n }            #=> 45
# Multiply some numbers
(5..10).reduce(1, :*)                          #=> 151200
# Same using a block
(5..10).inject(1) { |product, n| product * n } #=> 151200
# find the longest word
longest = %w{ cat sheep bear }.inject do |memo, word|
   memo.length > word.length ? memo : word
end
longest                                        #=> "sheep"
reject { |obj| block } → массив Показать исходный код
reject → перечислитель
static VALUE
enum_reject(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, reject_i, ary);

    return ary;
}

Возвращает массив для всех элементов enum, для которых заданная block возвращает false.

Если блок не задан, вместо этого возвращается перечислитель.

(1..10).reject { |i|  i % 3 == 0 }   #=> [1, 2, 4, 5, 7, 8, 10]

[1, 2, 3, 4, 5].reject { |num| num.even? } #=> [1, 3, 5]

См. также #find_all.

reverse_each(*args) { |item| block } → перечислитель Показать исходный код
reverse_each(*args) → перечислитель
static VALUE
enum_reverse_each(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary;
    long i;

    RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size);

    ary = enum_to_a(argc, argv, obj);

    for (i = RARRAY_LEN(ary); --i >= 0; ) {
        rb_yield(RARRAY_AREF(ary, i));
    }

    return obj;
}

Создаёт временный массив и обходит его в обратном порядке.

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

  (1..3).reverse_each { |v| p v }

produces:

  3
  2
  1
select { |obj| block } → массив Показать исходный код
select → перечислитель
static VALUE
enum_find_all(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, find_all_i, ary);

    return ary;
}

Возвращает массив, содержащий все элементы enum, для которых заданная block возвращает значение true.

Если блок не задан, вместо этого возвращается перечислитель.

(1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]

[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

См. также #reject.

slice_after(pattern) → перечислитель Показать исходный код
slice_after { |elt| bool } → перечислитель
static VALUE
enum_slice_after(int argc, VALUE *argv, VALUE enumerable)
{
    VALUE enumerator;
    VALUE pat = Qnil, pred = Qnil;

    if (rb_block_given_p()) {
        if (0 < argc)
            rb_raise(rb_eArgError, "both pattern and block are given");
        pred = rb_block_proc();
    }
    else {
        rb_scan_args(argc, argv, "1", &pat);
    }

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, rb_intern("sliceafter_enum"), enumerable);
    rb_ivar_set(enumerator, rb_intern("sliceafter_pat"), pat);
    rb_ivar_set(enumerator, rb_intern("sliceafter_pred"), pred);

    rb_block_call(enumerator, idInitialize, 0, 0, sliceafter_i, enumerator);
    return enumerator;
}

Создаёт перечислитель для каждого фрагмента элементов. Границы фрагментов определяются pattern и блоком.

Если pattern === elt возвращает true или блок возвращает true для элемента, то элемент является концом фрагмента.

Перечислитель и block вызываются от первого элемента к последнему элементу enum.

Результирующий перечислитель возвращает фрагменты элементов в виде массива. Поэтому метод each может вызываться следующим образом:

enum.slice_after(pattern).each { |ary| ... }
enum.slice_after { |elt| bool }.each { |ary| ... }

Также могут использоваться другие методы класса Enumerator и модуля Enumerable, такие как map, и т. д.

Например, строки с продолжением (строки, заканчивающиеся обратным слэшем) можно объединять следующим образом:

lines = ["foo\n", "bar\\\n", "baz\n", "\n", "qux\n"]
e = lines.slice_after(/(?<!\\)\n\z/)
p e.to_a
#=> [["foo\n"], ["bar\\\n", "baz\n"], ["\n"], ["qux\n"]]
p e.map {|ll| ll[0...-1].map {|l| l.sub(/\\\n\z/, "") }.join + ll.last }
#=>["foo\n", "barbaz\n", "\n", "qux\n"]
slice_before(pattern) → перечислитель Показать исходный код
slice_before { |elt| bool } → перечислитель
static VALUE
enum_slice_before(int argc, VALUE *argv, VALUE enumerable)
{
    VALUE enumerator;

    if (rb_block_given_p()) {
        if (argc != 0)
            rb_error_arity(argc, 0, 0);
        enumerator = rb_obj_alloc(rb_cEnumerator);
        rb_ivar_set(enumerator, rb_intern("slicebefore_sep_pred"), rb_block_proc());
    }
    else {
        VALUE sep_pat;
        rb_scan_args(argc, argv, "1", &sep_pat);
        enumerator = rb_obj_alloc(rb_cEnumerator);
        rb_ivar_set(enumerator, rb_intern("slicebefore_sep_pat"), sep_pat);
    }
    rb_ivar_set(enumerator, rb_intern("slicebefore_enumerable"), enumerable);
    rb_block_call(enumerator, idInitialize, 0, 0, slicebefore_i, enumerator);
    return enumerator;
}

Создаёт перечислитель для каждого фрагмента элементов. Начало фрагментов определяется pattern и блоком.

Если pattern === elt возвращает true или блок возвращает true для элемента, то элемент является началом фрагмента.

Перечислитель и block вызываются от первого элемента к последнему элементу enum. Результат для первого элемента игнорируется.

Результирующий перечислитель возвращает фрагменты элементов в виде массива. Поэтому метод each может вызываться следующим образом:

enum.slice_before(pattern).each { |ary| ... }
enum.slice_before { |elt| bool }.each { |ary| ... }

Также могут использоваться другие методы класса Enumerator и модуля Enumerable, такие как to_a, map, и т. д.

Например, итерация по записям ChangeLog может быть реализована следующим образом:

# iterate over ChangeLog entries.
open("ChangeLog") { |f|
  f.slice_before(/\A\S/).each { |e| pp e }
}

# same as above.  block is used instead of pattern argument.
open("ChangeLog") { |f|
  f.slice_before { |line| /\A\S/ === line }.each { |e| pp e }
}

“svn proplist -R” генерирует многострочный вывод для каждого файла. Их можно разбить на фрагменты следующим образом:

IO.popen([{"LC_ALL"=>"C"}, "svn", "proplist", "-R"]) { |f|
  f.lines.slice_before(/\AProp/).each { |lines| p lines }
}
#=> ["Properties on '.':\n", "  svn:ignore\n", "  svk:merge\n"]
#   ["Properties on 'goruby.c':\n", "  svn:eol-style\n"]
#   ["Properties on 'complex.c':\n", "  svn:mime-type\n", "  svn:eol-style\n"]
#   ["Properties on 'regparse.c':\n", "  svn:eol-style\n"]
#   ...

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

a = [0, 2, 3, 4, 6, 7, 9]
prev = a[0]
p a.slice_before { |e|
  prev, prev2 = e, prev
  prev2 + 1 != e
}.map { |es|
  es.length <= 2 ? es.join(",") : "#{es.first}-#{es.last}"
}.join(",")
#=> "0,2-4,6,7,9"

Однако локальные переменные следует использовать с осторожностью, если результирующий перечислитель перебирается два или более раз. Локальные переменные должны быть инициализированы для каждой итерации. Можно использовать Enumerator.new для этого.

# Word wrapping.  This assumes all characters have same width.
def wordwrap(words, maxwidth)
  Enumerator.new {|y|
    # cols is initialized in Enumerator.new.
    cols = 0
    words.slice_before { |w|
      cols += 1 if cols != 0
      cols += w.length
      if maxwidth < cols
        cols = w.length
        true
      else
        false
      end
    }.each {|ws| y.yield ws }
  }
end
text = (1..20).to_a.join(" ")
enum = wordwrap(text.split(/\s+/), 10)
puts "-"*10
enum.each { |ws| puts ws.join(" ") } # first enumeration.
puts "-"*10
enum.each { |ws| puts ws.join(" ") } # second enumeration generates same result as the first.
puts "-"*10
#=> ----------
#   1 2 3 4 5
#   6 7 8 9 10
#   11 12 13
#   14 15 16
#   17 18 19
#   20
#   ----------
#   1 2 3 4 5
#   6 7 8 9 10
#   11 12 13
#   14 15 16
#   17 18 19
#   20
#   ----------

mbox содержит серию писем, которые начинаются с строки Unix From. Поэтому каждое письмо может быть извлечено с помощью slice before строки Unix From.

# parse mbox
open("mbox") { |f|
  f.slice_before { |line|
    line.start_with? "From "
  }.each { |mail|
    unix_from = mail.shift
    i = mail.index("\n")
    header = mail[0...i]
    body = mail[(i+1)..-1]
    body.pop if body.last == "\n"
    fields = header.slice_before { |line| !" \t".include?(line[0]) }.to_a
    p unix_from
    pp fields
    pp body
  }
}

# split mails in mbox (slice before Unix From line after an empty line)
open("mbox") { |f|
  emp = true
  f.slice_before { |line|
    prevemp = emp
    emp = line == "\n"
    prevemp && line.start_with?("From ")
  }.each { |mail|
    mail.pop if mail.last == "\n"
    pp mail
  }
}
slice_when {|elt_before, elt_after| bool } → перечислитель Показать исходный код
static VALUE
enum_slice_when(VALUE enumerable)
{
    VALUE enumerator;
    VALUE pred;

    pred = rb_block_proc();

    enumerator = rb_obj_alloc(rb_cEnumerator);
    rb_ivar_set(enumerator, rb_intern("slicewhen_enum"), enumerable);
    rb_ivar_set(enumerator, rb_intern("slicewhen_pred"), pred);
    rb_ivar_set(enumerator, rb_intern("slicewhen_inverted"), Qfalse);

    rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator);
    return enumerator;
}

Создаёт перечислитель для каждого фрагмента элементов. Начало фрагментов определяется блоком.

Этот метод разделяет каждый фрагмент, используя смежные элементы, elt_before и elt_after, в перечислителе получателя. Этот метод разделяет фрагменты между elt_before и elt_after, где блок возвращает true.

Блок вызывается с длиной перечислителя получателя минус один.

Результирующий перечислитель возвращает фрагменты элементов в виде массива. Поэтому метод each может вызываться следующим образом:

enum.slice_when { |elt_before, elt_after| bool }.each { |ary| ... }

Также могут использоваться другие методы класса Enumerator и модуля Enumerable, такие как to_a, map, и т. д.

Например, возрастающую последовательность можно разбить на фрагменты следующим образом:

a = [1,2,4,9,10,11,12,15,16,19,20,21]
b = a.slice_when {|i, j| i+1 != j }
p b.to_a #=> [[1, 2], [4], [9, 10, 11, 12], [15, 16], [19, 20, 21]]
c = b.map {|a| a.length < 3 ? a : "#{a.first}-#{a.last}" }
p c #=> [[1, 2], [4], "9-12", [15, 16], "19-21"]
d = c.join(",")
p d #=> "1,2,4,9-12,15,16,19-21"

Близкие элементы (порог: 6) в отсортированном массиве можно разбить на фрагменты следующим образом:

a = [3, 11, 14, 25, 28, 29, 29, 41, 55, 57]
p a.slice_when {|i, j| 6 < j - i }.to_a
#=> [[3], [11, 14], [25, 28, 29, 29], [41], [55, 57]]

Возрастающую (неубывающую) последовательность можно разбить на фрагменты следующим образом:

a = [0, 9, 2, 2, 3, 2, 7, 5, 9, 5]
p a.slice_when {|i, j| i > j }.to_a
#=> [[0, 9], [2, 2, 3], [2, 7], [5, 9], [5]]

Последовательные чётные и нечётные числа можно разбить на фрагменты следующим образом: (Enumerable#chunk — это другой способ сделать это.)

a = [7, 5, 9, 2, 0, 7, 9, 4, 2, 0]
p a.slice_when {|i, j| i.even? != j.even? }.to_a
#=> [[7, 5, 9], [2, 0], [7, 9], [4, 2, 0]]

Абзацы (непустые строки с последующими пустыми строками) можно разбить на фрагменты следующим образом: (См. #chunk, чтобы игнорировать пустые строки.)

lines = ["foo\n", "bar\n", "\n", "baz\n", "qux\n"]
p lines.slice_when {|l1, l2| /\A\s*\z/ =~ l1 && /\S/ =~ l2 }.to_a
#=> [["foo\n", "bar\n", "\n"], ["baz\n", "qux\n"]]

#chunk_while делает то же самое, за исключением разделения, когда блок возвращает false вместо true.

sort → массив Показать исходный код
sort { |a, b| block } → массив
static VALUE
enum_sort(VALUE obj)
{
    return rb_ary_sort_bang(enum_to_a(0, 0, obj));
}

Возвращает массив, содержащий элементы enum, отсортированные.

Сравнения для сортировки будут выполняться с использованием собственного оператора элементов или с использованием необязательного блока кода.

Блок должен реализовывать сравнение между a и b и возвращать целое число меньше 0, когда b следует за a, целое число 0, когда a и b эквивалентны, или целое число больше 0, когда a следует за b.

Результат не гарантируется стабильным. Когда сравнение двух элементов возвращает 0, порядок элементов непредсказуем.

%w(rhea kea flea).sort           #=> ["flea", "kea", "rhea"]
(1..10).sort { |a, b| b <=> a }  #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

См. также #sort_by. Она реализует преобразование Шварца, которое полезно, когда вычисление или сравнение ключей является трудоёмким.

sort_by { |obj| block } → массив Показать исходный код
sort_by → перечислитель
static VALUE
enum_sort_by(VALUE obj)
{
    VALUE ary, buf;
    struct MEMO *memo;
    long i;
    struct sort_by_data *data;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

    if (RB_TYPE_P(obj, T_ARRAY) && RARRAY_LEN(obj) <= LONG_MAX/2) {
        ary = rb_ary_new2(RARRAY_LEN(obj)*2);
    }
    else {
        ary = rb_ary_new();
    }
    RBASIC_CLEAR_CLASS(ary);
    buf = rb_ary_tmp_new(SORT_BY_BUFSIZE*2);
    rb_ary_store(buf, SORT_BY_BUFSIZE*2-1, Qnil);
    memo = MEMO_NEW(0, 0, 0);
    OBJ_INFECT(memo, obj);
    data = (struct sort_by_data *)&memo->v1;
    RB_OBJ_WRITE(memo, &data->ary, ary);
    RB_OBJ_WRITE(memo, &data->buf, buf);
    data->n = 0;
    rb_block_call(obj, id_each, 0, 0, sort_by_i, (VALUE)memo);
    ary = data->ary;
    buf = data->buf;
    if (data->n) {
        rb_ary_resize(buf, data->n*2);
        rb_ary_concat(ary, buf);
    }
    if (RARRAY_LEN(ary) > 2) {
        RARRAY_PTR_USE(ary, ptr,
                      ruby_qsort(ptr, RARRAY_LEN(ary)/2, 2*sizeof(VALUE),
                                 sort_by_cmp, (void *)ary));
    }
    if (RBASIC(ary)->klass) {
        rb_raise(rb_eRuntimeError, "sort_by reentered");
    }
    for (i=1; i<RARRAY_LEN(ary); i+=2) {
        RARRAY_ASET(ary, i/2, RARRAY_AREF(ary, i));
    }
    rb_ary_resize(ary, RARRAY_LEN(ary)/2);
    RBASIC_SET_CLASS_RAW(ary, rb_cArray);
    OBJ_INFECT(ary, memo);

    return ary;
}

Сортирует enum, используя набор ключей, полученных путём сопоставления значений в enum через заданный блок.

Результат не гарантируется стабильным. Когда два ключа равны, порядок соответствующих элементов непредсказуем.

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

%w{apple pear fig}.sort_by { |word| word.length }
              #=> ["fig", "pear", "apple"]

Текущая реализация sort_by создаёт массив кортежей, содержащих исходный элемент коллекции и сопоставленное значение. Это делает sort_by довольно ресурсоёмким, когда наборы ключей простые.

require 'benchmark'

a = (1..100000).map { rand(100000) }

Benchmark.bm(10) do |b|
  b.report("Sort")    { a.sort }
  b.report("Sort by") { a.sort_by { |a| a } }
end

выдает:

user     system      total        real
Sort        0.180000   0.000000   0.180000 (  0.175469)
Sort by     1.980000   0.040000   2.020000 (  2.013586)

Однако, рассмотрим случай, когда сравнение ключей является нетривиальной операцией. Следующий код сортирует некоторые файлы по времени модификации, используя базовый метод sort.

files = Dir["*"]
sorted = files.sort { |a, b| File.new(a).mtime <=> File.new(b).mtime }
sorted   #=> ["mon", "tues", "wed", "thurs"]

Эта сортировка неэффективна: она генерирует два новых объекта File во время каждого сравнения. Несколько лучший подход — использовать метод Kernel#test для прямого получения времени модификации.

files = Dir["*"]
sorted = files.sort { |a, b|
  test(?M, a) <=> test(?M, b)
}
sorted   #=> ["mon", "tues", "wed", "thurs"]

Это всё ещё генерирует много ненужных объектов Time. Более эффективный подход — кешировать ключи сортировки (времена модификации в данном случае) перед сортировкой. Пользователи Perl часто называют этот подход преобразованием Шварца, по имени Рэнделла Шварца. Мы создаём временный массив, где каждый элемент представляет собой массив, содержащий наш ключ сортировки вместе с именем файла. Мы сортируем этот массив, а затем извлекаем имя файла из результата.

sorted = Dir["*"].collect { |f|
   [test(?M, f), f]
}.sort.collect { |f| f[1] }
sorted   #=> ["mon", "tues", "wed", "thurs"]

Именно это делает sort_by внутри.

sorted = Dir["*"].sort_by { |f| test(?M, f) }
sorted   #=> ["mon", "tues", "wed", "thurs"]
sum(init=0) → число Показать исходный код
sum(init=0) {|e| expr } → число
static VALUE
enum_sum(int argc, VALUE* argv, VALUE obj)
{
    struct enum_sum_memo memo;
    VALUE beg, end;
    int excl;

    if (rb_scan_args(argc, argv, "01", &memo.v) == 0)
        memo.v = LONG2FIX(0);

    memo.block_given = rb_block_given_p();

    memo.n = 0;
    memo.r = Qundef;

    if ((memo.float_value = RB_FLOAT_TYPE_P(memo.v))) {
        memo.f = RFLOAT_VALUE(memo.v);
        memo.c = 0.0;
    }

    if (RTEST(rb_range_values(obj, &beg, &end, &excl))) {
        if (!memo.block_given && !memo.float_value &&
                (FIXNUM_P(beg) || RB_TYPE_P(beg, T_BIGNUM)) &&
                (FIXNUM_P(end) || RB_TYPE_P(end, T_BIGNUM))) {
            return int_range_sum(beg, end, excl, memo.v);
        }
    }

    if (RB_TYPE_P(obj, T_HASH) &&
            rb_method_basic_definition_p(CLASS_OF(obj), id_each))
        hash_sum(obj, &memo);
    else
        rb_block_call(obj, id_each, 0, 0, enum_sum_i, (VALUE)&memo);

    if (memo.float_value) {
        return DBL2NUM(memo.f + memo.c);
    }
    else {
        if (memo.n != 0)
            memo.v = rb_fix_plus(LONG2FIX(memo.n), memo.v);
        if (memo.r != Qundef) {
            /* r can be an Integer when mathn is loaded */
            if (FIXNUM_P(memo.r))
                memo.v = rb_fix_plus(memo.r, memo.v);
            else if (RB_TYPE_P(memo.r, T_BIGNUM))
                memo.v = rb_big_plus(memo.r, memo.v);
            else
                memo.v = rb_rational_plus(memo.r, memo.v);
        }
        return memo.v;
    }
}

Возвращает сумму элементов в Enumerable.

Если задан блок, блок применяется к каждому элементу перед сложением.

Если enum пуст, возвращает init.

Например:

{ 1 => 10, 2 => 20 }.sum {|k, v| k * v }  #=> 50
(1..10).sum                               #=> 55
(1..10).sum {|v| v * 2 }                  #=> 110
[Object.new].each.sum                     #=> TypeError

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

{ 1 => 10, 2 => 20 }.sum([])                   #=> [1, 10, 2, 20]
"a\nb\nc".each_line.lazy.map(&:chomp).sum("")  #=> "abc"

#sum метод может не учитывать переопределение метода “+” таких методов, как Integer#+.

take(n) → массив Показать исходный код
static VALUE
enum_take(VALUE obj, VALUE n)
{
    struct MEMO *memo;
    VALUE result;
    long len = NUM2LONG(n);

    if (len < 0) {
        rb_raise(rb_eArgError, "attempt to take negative size");
    }

    if (len == 0) return rb_ary_new2(0);
    result = rb_ary_new2(len);
    memo = MEMO_NEW(result, 0, len);
    rb_block_call(obj, id_each, 0, 0, take_i, (VALUE)memo);
    return result;
}

Возвращает первые n элементов из enum.

a = [1, 2, 3, 4, 5, 0]
a.take(3)             #=> [1, 2, 3]
a.take(30)            #=> [1, 2, 3, 4, 5, 0]
take_while { |obj| блок } → массив Показать исходный код
take_while → итератор
static VALUE
enum_take_while(VALUE obj)
{
    VALUE ary;

    RETURN_ENUMERATOR(obj, 0, 0);
    ary = rb_ary_new();
    rb_block_call(obj, id_each, 0, 0, take_while_i, ary);
    return ary;
}

Передаёт элементы в блок, пока блок возвращает nil или false, затем останавливает итерацию и возвращает массив всех предыдущих элементов.

Если блок не задан, возвращается итератор вместо него.

a = [1, 2, 3, 4, 5, 0]
a.take_while { |i| i < 3 }   #=> [1, 2]
to_a(*args) → массив Показать исходный код
static VALUE
enum_to_a(int argc, VALUE *argv, VALUE obj)
{
    VALUE ary = rb_ary_new();

    rb_block_call(obj, id_each, argc, argv, collect_all, ary);
    OBJ_INFECT(ary, obj);

    return ary;
}

Возвращает массив, содержащий элементы из enum.

(1..7).to_a                       #=> [1, 2, 3, 4, 5, 6, 7]
{ 'a'=>1, 'b'=>2, 'c'=>3 }.to_a   #=> [["a", 1], ["b", 2], ["c", 3]]

require 'prime'
Prime.entries 10                  #=> [2, 3, 5, 7]
to_h(*args) → хэш Показать исходный код
static VALUE
enum_to_h(int argc, VALUE *argv, VALUE obj)
{
    VALUE hash = rb_hash_new();
    rb_block_call(obj, id_each, argc, argv, enum_to_h_i, hash);
    OBJ_INFECT(hash, obj);
    return hash;
}

Возвращает результат интерпретации enum как списка [key, value] пар.

%i[hello world].each_with_index.to_h
  # => {:hello => 0, :world => 1}
to_set(klass = Set, *args, &block) Показать исходный код
# File lib/set.rb, line 811
def to_set(klass = Set, *args, &block)
  klass.new(self, *args, &block)
end

Создаёт множество из перечисляемого объекта с заданными аргументами. Необходимо использовать +require “set”+, чтобы использовать этот метод.

uniq → новый_массив Показать исходный код
uniq { |item| ... } → новый_массив
static VALUE
enum_uniq(VALUE obj)
{
    VALUE hash, ret;
    rb_block_call_func *const func =
        rb_block_given_p() ? uniq_iter : uniq_func;

    hash = rb_obj_hide(rb_hash_new());
    rb_block_call(obj, id_each, 0, 0, func, hash);
    ret = rb_hash_values(hash);
    rb_hash_clear(hash);
    return ret;
}

Возвращает новый массив, удаляя дублирующие значения в self.

См. также Array#uniq.

zip(arg, ...) → массив_массивов Показать исходный код
zip(arg, ...) { |arr| блок } → nil
static VALUE
enum_zip(int argc, VALUE *argv, VALUE obj)
{
    int i;
    ID conv;
    struct MEMO *memo;
    VALUE result = Qnil;
    VALUE args = rb_ary_new4(argc, argv);
    int allary = TRUE;

    argv = RARRAY_PTR(args);
    for (i=0; i<argc; i++) {
        VALUE ary = rb_check_array_type(argv[i]);
        if (NIL_P(ary)) {
            allary = FALSE;
            break;
        }
        argv[i] = ary;
    }
    if (!allary) {
        static const VALUE sym_each = STATIC_ID2SYM(id_each);
        CONST_ID(conv, "to_enum");
        for (i=0; 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]));
            }
            argv[i] = rb_funcallv(argv[i], conv, 1, &sym_each);
        }
    }
    if (!rb_block_given_p()) {
        result = rb_ary_new();
    }

    /* TODO: use NODE_DOT2 as memo(v, v, -) */
    memo = MEMO_NEW(result, args, 0);
    rb_block_call(obj, id_each, 0, 0, allary ? zip_ary : zip_i, (VALUE)memo);

    return result;
}

Берёт один элемент из enum и объединяет соответствующие элементы из каждого args. Это генерирует последовательность n-элементных массивов, где n — на один больше, чем количество аргументов. Длина результирующей последовательности будет enum#size. Если размер любого аргумента меньше enum#size, используются значения nil. Если задан блок, он вызывается для каждого выходного массива, в противном случае возвращается массив массивов.

a = [ 4, 5, 6 ]
b = [ 7, 8, 9 ]

a.zip(b)                 #=> [[4, 7], [5, 8], [6, 9]]
[1, 2, 3].zip(a, b)      #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
[1, 2].zip(a, b)         #=> [[1, 4, 7], [2, 5, 8]]
a.zip([1, 2], [8])       #=> [[4, 1, 8], [5, 2, nil], [6, nil, nil]]

c = []
a.zip(b) { |x, y| c << x + y }  #=> nil
c                               #=> [11, 13, 15]

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

Spec-Zone.ru

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