非常教程

Ruby 2.4参考手册

枚举 | Enumerable

Enumerable

Enumerable混入提供了集合类与几个遍历和搜索方法,以及排序的功能。该类必须提供一种方法each,该方法会生成该集合的连续成员。如果Enumerable#max#min或者#sort被使用,集合中的对象也必须实现一个有意义的<=>操作符,因为这些方法依赖于集合成员之间的顺序。

公共实例方法

all? { |obj| block } → true or false Show source

将集合的每个元素传递给给定的块。 如果该块永不返回false或nil,则该方法返回true。 如果没有给出块,Ruby会添加一个{| obj |的隐式块 obj}这将导致所有? 当所有集合成员都不是false或nil时返回true。

%w[ant bear cat].all? { |word| word.length >= 3 } #=> true
%w[ant bear cat].all? { |word| word.length >= 4 } #=> false
[nil, true, 99].all?                              #=> false
[].all?                                           #=> true
static VALUE
enum_all(VALUE obj)
{
    struct MEMO *memo = MEMO_NEW(Qtrue, 0, 0);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
    return memo->v1;
}

any? { |obj| block } → true or false Show source

将集合的每个元素传递给给定的块。 如果该块返回的值不是false或nil,该方法返回true。 如果没有给出块,Ruby会添加一个{| obj |的隐式块 obj}(导致任何?返回true) 如果至少其中一个收集成员不是假或零,则返回true。

%w[ant bear cat].any? { |word| word.length >= 3 } #=> true
%w[ant bear cat].any? { |word| word.length >= 4 } #=> true
[nil, true, 99].any?                              #=> true
[].any?                                           #=> false
static VALUE
enum_any(VALUE obj)
{
    struct MEMO *memo = MEMO_NEW(Qfalse, 0, 0);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo);
    return memo->v1;
}

chunk { |elt| ... } → an_enumerator Show source

枚举项目,根据块的返回值将它们组合在一起。

返回相同块值的连续元素被分块在一起。

例如,连续的偶数和奇数可以如下分块。

[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 and :_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可以用来让事务进入他们自己的块。例如,您可以将自己包含网址的行放入其中,并将其余的行组合在一起,如下所示:

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

如果没有给出块,则返回块的枚举器。

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;
}

chunk_while {|elt_before, elt_after| bool } → an_enumerator Show source

为每个分块元素创建一个枚举器。块的开始由块定义。

该方法使用接收者枚举器中的相邻元素elt_before和elt_after分割每个块。 此方法在elt_before和elt_after之间拆分块,其中块返回false。

该块被称为接收者枚举器的长度减1。

结果枚举器将分块元素作为数组生成。 所以每个方法可以被调用如下:

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]]

相邻的evens和odds可以如下分块:(可枚举#块是另一种方式来做到这一点。)

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时分割。

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;
}

collect { |obj| block } → array Show source

collect → an_enumerator

对于枚举中的每个元素,返回一个运行的结果的新数组。

如果没有给出块,则返回一个枚举器。

(1..4).map { |i| i*i }      #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
static VALUE
enum_collect(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

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

    return ary;
}

collect_concat { |obj| block } → array Show source

collect_concat → an_enumerator

对于枚举中的 每个元素,返回一个具有连续运行结果的新数组。

如果没有给出块,则返回一个枚举器。

[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]
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;
}

count → int Show source

count(item) → int

count { |obj| block } → int

通过枚举返回枚举中的项目数。 如果给出了一个参数,那么枚举中等于项目的项目数就被计数。 如果给出了一个块,它会计算产生真值的元素的数量。

ary = [1, 2, 4, 2]
ary.count               #=> 4
ary.count(2)            #=> 2
ary.count{ |x| x%2==0 } #=> 3
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 INT2NUM(memo->u3.cnt);
}

cycle(n=nil) { |obj| block } → nil Show source

cycle(n=nil) → an_enumerator

如果没有给出nil或nil,则为n的每个元素重复调用n次或永远的块。 如果给出了一个非正数,或者该集合是空的,则什么也不做。 如果循环完成而没有被中断,则返回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.
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;
}

detect(ifnone = nil) { |obj| block } → obj or nil Show source

detect(ifnone = nil) → an_enumerator

将枚举中的每个条目传递给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
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;
}

drop(n) → array Show source

枚举中 删除前n个元素,并返回数组中的其余元素。

a = [1, 2, 3, 4, 5, 0]
a.drop(3)             #=> [4, 5, 0]
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;
}

drop_while { |obj| block } → array Show source

drop_while → an_enumerator

将元素删除,但不包括块返回nil或false的第一个元素,并返回包含其余元素的数组。

如果没有给出块,则返回一个枚举器。

a = [1, 2, 3, 4, 5, 0]
a.drop_while { |i| i < 3 }   #=> [3, 4, 5, 0]
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;
}

each_cons(n) { ... } → nil Show source

each_cons(n) → an_enumerator

迭代每个连续<n>元素数组的给定块。如果没有给出块,则返回一个枚举器。

e.g.:

(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]
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;
}

each_entry { |obj| block } → enum Show source

each_entry → an_enumerator

为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
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;
}

each_slice(n) { ... } → nil Show source

each_slice(n) → an_enumerator

迭代给定每个<n>元素片段的块。如果没有给出块,则返回一个枚举器。

(1..10).each_slice(3) { |a| p a }
# outputs below
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]
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;
}

each_with_index(*args) { |obj, i| block } → enum Show source

each_with_index(*args) → an_enumerator

使用enum中的每个项目调用具有两个参数的,即项目及其索引。给定的参数传递给each()。

如果没有给出块,则返回一个枚举器。

hash = Hash.new
%w(cat dog wombat).each_with_index { |item, index|
  hash[item] = index
}
hash   #=> {"cat"=>0, "dog"=>1, "wombat"=>2}
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;
}

each_with_object(obj) { |(*args), memo_obj| ... } → obj Show source

each_with_object(obj) → an_enumerator

用给定的任意对象遍历每个元素的给定块,并返回最初给定的对象。

如果没有给出块,则返回一个枚举器。

evens = (1..10).each_with_object([]) { |i, a| a << i*2 }
#=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
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;
}

entries(*args) → array Show source

返回包含枚举中 项目的数组。

(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]
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;
}

find(ifnone = nil) { |obj| block } → obj or nil Show source

find(ifnone = nil) → an_enumerator

枚举中的每个条目传递给阻止。返回第一个不是假的。如果没有对象匹配,则调用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
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;
}

find_all { |obj| block } → array Show source

find_all → an_enumerator

返回一个数组,其中包含所有给定块返回true值的枚举元素。

如果没有给出块,则返回一个枚举器。

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

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

另见#reject。

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;
}

find_index(value) → int or nil Show source

find_index { |obj| block } → int or nil

find_index → an_enumerator

枚举中的 每个条目与值进行比较 或通过阻止。返回评估值为非假的第一个索引。如果没有对象匹配,则返回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
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;
}

first → obj or nil Show source

first(n) → an_array

返回可枚举的第一个元素或前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)              #=> []
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;
    }
}

flat_map { |obj| block } → array Show source

flat_map → an_enumerator

对于枚举中的每个元素,返回一个具有连续运行结果的新数组。

如果没有给出块,则返回一个枚举器。

[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]
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;
}

grep(pattern) → array Show source

grep(pattern) { |obj| block } → array

返回Pattern ===元素的enum中每个元素的数组。 如果提供了可选块,则将每个匹配元素传递给它,并将块的结果存储在输出数组中。

(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]
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;
}

grep_v(pattern) → array Show source

grep_v(pattern) { |obj| block } → array

#grep的倒置版本。 返回枚举中不是Pattern ===元素的每个元素的数组。

(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]
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;
}

group_by { |obj| block } → a_hash Show source

group_by → an_enumerator

按块的结果对集合进行分组。返回一个散列,其中的键是块的评估结果,值是集合中与该键相对应的元素数组。

如果没有给出块,则返回枚举器。

(1..6).group_by { |i| i%3 }   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
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;
}

include?(obj) → true or false Show source

如果枚举的任何成员等于obj,则返回true。 平等使用==进行测试。

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
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;
}

inject(initial, sym) → obj Show source

inject(sym) → obj

inject(initial) { |memo, obj| block } → obj

inject { |memo, obj| block } → obj

通过应用二进制操作来组合枚举的所有元素,该操作由块或命名方法或运算符的符号指定。

inject reduce 方法是别名。两者都没有性能优势。

如果指定了一个块,那么对于枚举中的每个元素,该块将传递一个累加器值(备注)和该元素。如果您改为指定一个符号,则集合中的每个元素都将被传递给指定的备忘录方法。无论哪种情况,结果都会成为备忘录的新值。在迭代结束时,备忘录的最终值是该方法的返回值。

如果您没有明确指定备忘录 初始值,则集合的第一个元素将用作备忘录 的初始值。

# 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"
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;
}

lazy → lazy_enumerator Show source

返回一个惰性枚举器,其方法只在需要时映射/收集,flat_map / collect_concat,select / find_all,reject,grep,#grep_v,zip,take,#take_while,drop和#drop_枚举值。但是,如果给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
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 { |obj| block } → array Show source

map → an_enumerator

对于枚举中的 每个元素,返回一个运行的结果的新数组。

如果没有给出块,则返回一个枚举器。

(1..4).map { |i| i*i }      #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]
static VALUE
enum_collect(VALUE obj)
{
    VALUE ary;

    RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size);

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

    return ary;
}

max → obj Show source

max { |a, b| block } → obj

max(n) → array

max(n) { |a, b| block } → array

以最大值返回枚举中的对象。第一种形式假定所有对象都实现Comparable; 第二个使用该块返回<=> 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]
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;
}

max_by {|obj| block } → obj Show source

max_by → an_enumerator

max_by(n) {|obj| block } → obj

max_by(n) → an_enumerator

返回枚举 中给出给定块的最大值的对象。

如果没有给出块,则返回一个枚举器。

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] }
#=> *
#   ***
#   ******
#   ***********
#   ******************
#   *****************************
#   *****************************************
#   ****************************************************
#   ***************************************************************
#   ********************************************************************
#   ***********************************************************************
#   ***********************************************************************
#   **************************************************************
#   ****************************************************
#   ***************************************
#   ***************************
#   ******************
#   ***********
#   *******
#   ***
#   *
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;
}

member?(obj) → true or false Show source

如果枚举的任何成员等于obj,则返回true。 平等使用==进行测试。

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
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;
}

min → obj Show source

min { |a, b| block } → obj

min(n) → array

min(n) { |a, b| block } → array

以最小值返回枚举中的对象。 第一种形式假定所有对象都实现了Comparable; 第二个使用该块返回<=> 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]
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;
}

min_by {|obj| block } → obj Show source

min_by → an_enumerator

min_by(n) {|obj| block } → array

min_by(n) → an_enumerator

返回枚举 中给出给定块的最小值的对象。

如果没有给出块,则返回一个枚举器。

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"]
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;
}

minmax → min, max()

minmax { |a, b| block } → min, max

返回包含enumerable中的最小值和最大值的两个元素数组。 第一种形式假定所有对象都实现了Comparable; 第二个使用该块返回<=> b。

a = %w(albatross dog horse)
a.minmax                                  #=> ["albatross", "horse"]
a.minmax { |a, b| a.length <=> b.length } #=> ["dog", "albatross"]
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);
}

minmax_by { |obj| block } → min, max()

minmax_by → an_enumerator

返回包含enum 中的对象的两个元素数组,分别对应给定块中的最小值和最大值。

如果没有给出块,则返回一个枚举器。

a = %w(albatross dog horse)
a.minmax_by { |x| x.length }   #=> ["dog", "albatross"]
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);
}

none? { |obj| block } → true or false Show source

将集合的每个元素传递给给定的块。 如果该块从不对所有元素返回true,则该方法返回true。 如果该块没有给出,none? 只有在没有任何收集成员为真的情况下才会返回true。

%w{ant bear cat}.none? { |word| word.length == 5 } #=> true
%w{ant bear cat}.none? { |word| word.length >= 4 } #=> false
[].none?                                           #=> true
[nil].none?                                        #=> true
[nil, false].none?                                 #=> true
[nil, false, true].none?                           #=> false
static VALUE
enum_none(VALUE obj)
{
    struct MEMO *memo = MEMO_NEW(Qtrue, 0, 0);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo);
    return memo->v1;
}

one? { |obj| block } → true or false Show source

将集合的每个元素传递给给定的块。 如果该块只返回一次真,该方法返回true。 如果没有给出块,one? 只有当其中一个收集成员为真时才会返回true。

%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
[ nil, true, 99 ].one?                             #=> false
[ nil, true, false ].one?                          #=> true
static VALUE
enum_one(VALUE obj)
{
    struct MEMO *memo = MEMO_NEW(Qundef, 0, 0);
    VALUE result;

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

partition { |obj| block } → true_array, false_array ()

partition → an_enumerator

返回两个数组,第一个包含枚举的元素,块的评估结果为true,第二个包含其余部分。

如果没有给出块,则返回一个枚举器。

(1..6).partition { |v| v.even? }  #=> [[2, 4, 6], [1, 3, 5]]
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);
}

reduce(initial, sym) → obj Show source

reduce(sym) → obj

reduce(initial) { |memo, obj| block } → obj

reduce { |memo, obj| block } → obj

通过应用二进制操作来组合枚举 的所有元素,该操作由块或命名方法或运算符的符号指定。

injectreduce 方法是别名。两者都没有性能优势。

如果指定了一个块,那么对于枚举中的每个元素,该块将传递一个累加器值(备注)和该元素。如果您改为指定一个符号,则集合中的每个元素都将被传递给指定的备忘录方法。无论哪种情况,结果都会成为备忘录的新值。在迭代结束时,备忘录的最终值是该方法的返回值。

如果您没有明确指定备忘录初始 值,则集合的第一个元素将用作备忘录的初始值。

# 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"
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;
}

reject { |obj| block } → array Show source

reject → an_enumerator

返回给定块返回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。

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;
}

reverse_each(*args) { |item| block } → enum Show source

reverse_each(*args) → an_enumerator

构建一个临时数组并以相反的顺序遍历该数组。

如果没有给出块,则返回一个枚举器。

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

produces:

  3
  2
  1
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;
}

select { |obj| block } → array Show source

select → an_enumerator

返回一个数组,其中包含所有给定块返回true值的枚举元素。

如果没有给出块,则返回一个枚举器。

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

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

另见#reject。

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;
}

slice_after(pattern) → an_enumerator Show source

slice_after { |elt| bool } → an_enumerator

为每个分块元素创建一个枚举器。块的末端由模式 和块定义。

如果pattern === elt返回true,或者该块返回true,则该元素是块的结尾。

===和块从第一个元素调用到枚举的最后一个元素。

结果枚举器将分块元素作为数组生成。所以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"]
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;
}

slice_before(pattern) → an_enumerator Show source

slice_before { |elt| bool } → an_enumerator

为每个分块元素创建一个枚举器。块的开始由模式 和块定义。

如果pattern === elt返回true,或者该块返回true,则该元素是块的开头。

===和块从第一个元素调用到枚举的最后一个元素。 第一个元素的结果被忽略。

结果枚举器将分块元素作为数组生成。所以each方法可以调用如下:

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

枚举器类和可枚举模块的其它方法,诸如to_amap等,也可以使用。

例如,通过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 Line开头的邮件。因此,每个邮件可以在Unix From line之前通过分片来提取。

# 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
  }
}
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;
}

slice_when {|elt_before, elt_after| bool } → an_enumerator Show source

为每个分块元素创建一个枚举器。块的开始由块定义。

该方法使用接收者枚举器中的相邻元素elt_before和elt_after分割每个块。 此方法在elt_before和elt_after之间拆分块,其中块返回true。

该块被称为接收者枚举器的长度减1。

结果枚举器将分块元素作为数组生成。 所以每个方法可以被调用如下:

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

枚举器类和可枚举模块的其它方法,诸如to_amap等,也可以使用。

例如,逐个递增的子序列可以如下分块:

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]]

相邻的evens和odds可以如下分块:(可枚举#块是另一种方式来做到这一点。)

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时分割。

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;
}

sort → array Show source

sort { |a, b| block } → array

返回包含枚举 排序项目的数组。

使用项目自己的<=>操作员或使用可选代码块进行排序比较。

块必须实现a和b之间的比较,并返回一个小于0的整数,当b跟随a时,a和b相等时为0,或者当a跟随b时返回大于0的整数。

结果不能保证稳定。当两个元素的比较返回时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。它实现了Schwartzian变换,当关键计算或比较昂贵时,该变换非常有用。

static VALUE
enum_sort(VALUE obj)
{
    return rb_ary_sort_bang(enum_to_a(0, 0, obj));
}

sort_by { |obj| block } → array Show source

sort_by → an_enumerator

使用通过给定块中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对象。 更有效的技术是在排序之前缓存排序键(在这种情况下为修改时间)。 在Randal Schwartz之后,Perl用户经常称这种方法为Schwartzian变换。 我们构造一个临时数组,其中每个元素都是一个包含我们的排序键以及文件名的数组。 我们对这个数组进行排序,然后从结果中提取文件名。

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"]
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;
}

sum(init=0) → number Show source

sum(init=0) {|e| expr } → number

Returns the sum of elements in an Enumerable.

如果给出了一个块,则该块在添加之前应用于每个元素。

如果枚举 为空,则返回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

通过显式初始化 参数可以将此方法用于非数字对象。

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

#sum方法可能不会重新定义诸如Integer#+之类的“+”方法。

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;
    }
}

take(n) → array Show source

返回枚举 中的前n个元素。

a = [1, 2, 3, 4, 5, 0]
a.take(3)             #=> [1, 2, 3]
a.take(30)            #=> [1, 2, 3, 4, 5, 0]
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;
}

take_while { |obj| block } → array Show source

take_while → an_enumerator

将元素传递给块,直到块返回nil或false,然后停止迭代并返回所有先前元素的数组。

如果没有给出块,则返回一个枚举器。

a = [1, 2, 3, 4, 5, 0]
a.take_while { |i| i < 3 }   #=> [1, 2]
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;
}

to_a(*args) → array Show source

返回包含枚举中 项目的数组。

(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]
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;
}

to_h(*args) → hash Show source

返回将枚举 解释为对列表的结果[key, value]

%[hello world].each_with_index.to_h
  # => {:hello => 0, :world => 1}
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;
}

to_set(klass = Set, *args, &block) Show source

使用给定参数从可枚举对象中创建一个集合。需要+需要“设置”+才能使用此方法。

# File lib/set.rb, line 727
def to_set(klass = Set, *args, &block)
  klass.new(self, *args, &block)
end

uniq → new_ary Show source

uniq { |item| ... } → new_ary

通过删除重复值来返回新数组self

另见Array#uniq。

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;
}

zip(arg, ...) → an_array_of_array Show source

zip(arg, ...) { |arr| block } → nil

从枚举中取一个元素并合并每个参数的相应元素。 这会生成一个n元素数组序列,其中n比参数个数多一个。 结果序列的长度将是enum#size。 如果任何参数的大小小于enum#大小,则会提供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]
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) {
        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_funcall(argv[i], conv, 1, ID2SYM(id_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;
}

枚举 | Enumerable相关

Ruby 2.4

Ruby 是一种面向对象、命令式、函数式、动态的通用编程语言,是世界上最优美而巧妙的语言。

主页 https://www.ruby-lang.org/
源码 https://github.com/ruby/ruby
版本 2.4
发布版本 2.4.1

Ruby 2.4目录

1.缩略 | Abbrev
2.ARGF
3.数组 | Array
4.Base64
5.基本对象 | BasicObject
6.基准测试 | Benchmark
7.BigDecimal
8.绑定 | Binding
9.CGI
10.类 | Class
11.比较 | Comparable
12.负责 | Complex
13.计算续体 | Continuation
14.覆盖 | Coverage
15.CSV
16.日期 | Date
17.日期时间 | DateTime
18.DBM
19.代理 | Delegator
20.摘要 | Digest
21.Dir
22.DRb
23.编码 | Encoding
24.枚举 | Enumerable
25.枚举 | Enumerator
26.ENV
27.ERB
28.错误 | Errors
29.Etc
30.期望值 | Exception
31.错误类 | FalseClass
32.Fiber
33.Fiddle
34.文件 | File
35.文件实用程序 | FileUtils
36.查找 | Find
37.浮点 | Float
38.Forwardable
39.GC
40.GDBM
41.GetoptLong
42.Hash
43.Integer
44.IO
45.IPAddr
46.JSON
47.Kernel
48.语言 | 3Language
49.记录 | Logger
50.编排 | Marshal
51.MatchData
52.数学 | Math
53.矩阵 | Matrix
54.方法 | Method
55.模型 | Module
56.监控 | Monitor
57. 互斥 | Mutex
58.Net
59.Net::FTP
60.Net::HTTP
61.Net::IMAP
62.Net::SMTP
63.NilClass
64.数字 | Numeric
65.对象 | Object
66.ObjectSpace
67.Observable
68.Open3
69.OpenSSL
70.OpenStruct
71.OpenURI
72.OptionParser
73.路径名 | Pathname
74.完整输出 | PrettyPrint
75.Prime
76.Proc
77.过程 | Process
78.PStore
79.PTY
80.队列 | Queue
81.随机 | Random
82.范围 | Range
83.合理的 | Rational
84.Readline
85.Regexp
86.Resolv
87.Ripper
88.RubyVM
89.Scanf
90.SDBM
91.SecureRandom
92.Set
93.Shell
94.信号 | Signal
95.Singleton
96.套接字 | Socket
97.字符串 | String
98.StringIO
99.StringScanner
100.结构 | Struct