Enumerable 模块
此处内容¶ ↑
Enumerable 模块提供对集合类有用的方法,用于
查询方法¶ ↑
这些方法返回有关 Enumerable 的信息,而不是元素本身
-
all?
:如果所有元素都满足指定条件,则返回true
;否则返回false
。 -
any?
:如果任何元素满足指定条件,则返回true
;否则返回false
。 -
none?
:如果没有元素满足指定条件,则返回true
;否则返回false
。 -
one?
:如果恰好有一个元素满足指定条件,则返回true
;否则返回false
。 -
count
:返回元素的数量,如果给定,则基于参数或块条件。
获取方法¶ ↑
这些方法从 Enumerable 中返回条目,而不修改它
前导、尾随或所有元素:
-
first
:返回第一个元素或前导元素。 -
take
:返回指定数量的前导元素。 -
drop
:返回指定数量的尾随元素。 -
take_while
:返回由给定块指定的前导元素。 -
drop_while
:返回由给定块指定的尾随元素。
最小值和最大值元素:
-
min
:返回元素中值最小的元素,由<=>
或给定的块确定。 -
max
:返回元素中值最大的元素,由<=>
或给定的块确定。 -
min_by
:返回最小的元素,由给定的块确定。 -
max_by
:返回最大的元素,由给定的块确定。 -
minmax_by
:返回最小元素和最大元素,由给定的块确定。
组、切片和分区:
-
partition
:返回根据给定块确定的两个新数组中分区的元素。 -
slice_after
:返回一个新的Enumerator
,其条目是self
的一个分区,基于给定的object
或给定的块。 -
slice_before
:返回一个新的Enumerator
,其条目是self
的一个分区,基于给定的object
或给定的块。 -
slice_when
:返回一个新的Enumerator
,其条目是基于给定块的self
的一个分区。 -
chunk
:返回根据给定块指定的块中组织的元素。 -
chunk_while
:返回根据给定块指定的块中组织的元素。
搜索和筛选方法¶ ↑
这些方法返回满足指定条件的元素
-
find_index
:返回由给定对象或块选择的元素的索引。 -
reject
:返回未被块拒绝的元素。 -
uniq
:返回不是重复项的元素。
排序方法¶ ↑
这些方法返回按顺序排列的元素
迭代方法¶ ↑
-
each_entry
:使用每个连续元素(与每个元素略有不同)调用块。 -
each_with_index
:使用每个连续元素及其索引调用块。 -
each_with_object
:使用每个连续元素和给定对象调用块。 -
each_slice
:使用连续的非重叠切片调用块。 -
each_cons
:使用连续的重叠切片调用块。(不同于each_slice
)。 -
reverse_each
:使用每个连续元素(按相反顺序)调用块。
其他方法¶ ↑
-
filter_map
:返回块返回的真值对象。 -
flat_map
,collect_concat
:返回块返回的扁平化对象。 -
grep
:返回由给定对象或给定块返回的对象选出的元素。 -
grep_v
:返回由给定对象或给定块返回的对象选出的元素。 -
sum
:使用 + 方法返回元素的总和。 -
zip
:将每个元素与其他可枚举元素组合;返回 n 元组或使用每个元素调用块。 -
cycle
:使用每个元素调用块,循环重复。
用法¶ ↑
在集合类中使用 Enumerable 模块
-
包括它
include Enumerable
-
实现方法
#each
,它必须生成集合的连续元素。几乎任何 Enumerable 方法都会调用该方法。
示例
class Foo include Enumerable def each yield 1 yield 1, 2 yield end end Foo.new.each_entry{ |element| p element }
输出
1 [1, 2] nil
Ruby 类中的 Enumerable¶ ↑
这些 Ruby 核心类包括(或扩展)Enumerable
这些 Ruby 标准库类包括 Enumerable
Enumerable 中几乎所有方法都会调用包含类中的方法 #each
-
Hash#each
将下一个键值对作为 2 元素的Array
产生。 -
Struct#each
将下一个名称值对作为 2 元素的Array
产生。 -
对于上述其他类,
#each
将从集合中产生下一个对象。
关于示例¶ ↑
Enumerable 方法的示例代码片段
公共实例方法
返回是否每个元素都满足给定的条件。
如果 self
没有元素,则返回 true
,并且不使用参数或块。
如果没有参数和块,则返回是否每个元素都为真值
(1..4).all? # => true %w[a b c d].all? # => true [1, 2, nil].all? # => false ['a','b', false].all? # => false [].all? # => true
如果带有参数 pattern
且没有块,则返回对于每个元素 element
,是否 pattern === element
(1..4).all?(Integer) # => true (1..4).all?(Numeric) # => true (1..4).all?(Float) # => false %w[bar baz bat bam].all?(/ba/) # => true %w[bar baz bat bam].all?(/bar/) # => false %w[bar baz bat bam].all?('ba') # => false {foo: 0, bar: 1, baz: 2}.all?(Array) # => true {foo: 0, bar: 1, baz: 2}.all?(Hash) # => false [].all?(Integer) # => true
如果给出了块,则返回该块是否为每个元素返回真值
(1..4).all? {|element| element < 5 } # => true (1..4).all? {|element| element < 4 } # => false {foo: 0, bar: 1, baz: 2}.all? {|key, value| value < 3 } # => true {foo: 0, bar: 1, baz: 2}.all? {|key, value| value < 2 } # => false
static VALUE enum_all(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo = MEMO_ENUM_NEW(Qtrue); WARN_UNUSED_BLOCK(argc); rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo); return memo->v1; }
返回是否任何元素都满足给定的条件。
如果 self
没有元素,则返回 false
,并且不使用参数或块。
如果没有参数和块,则返回是否任何元素都为真值
(1..4).any? # => true %w[a b c d].any? # => true [1, false, nil].any? # => true [].any? # => false
如果带有参数 pattern
且没有块,则返回对于任何元素 element
,是否 pattern === element
[nil, false, 0].any?(Integer) # => true [nil, false, 0].any?(Numeric) # => true [nil, false, 0].any?(Float) # => false %w[bar baz bat bam].any?(/m/) # => true %w[bar baz bat bam].any?(/foo/) # => false %w[bar baz bat bam].any?('ba') # => false {foo: 0, bar: 1, baz: 2}.any?(Array) # => true {foo: 0, bar: 1, baz: 2}.any?(Hash) # => false [].any?(Integer) # => false
如果给出了块,则返回该块是否为任何元素返回真值
(1..4).any? {|element| element < 2 } # => true (1..4).any? {|element| element < 1 } # => false {foo: 0, bar: 1, baz: 2}.any? {|key, value| value < 1 } # => true {foo: 0, bar: 1, baz: 2}.any? {|key, value| value < 0 } # => false
static VALUE enum_any(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo = MEMO_ENUM_NEW(Qfalse); WARN_UNUSED_BLOCK(argc); rb_block_call(obj, id_each, 0, 0, ENUMFUNC(any), (VALUE)memo); return memo->v1; }
从该枚举器和给定的枚举器生成枚举器对象。
e = (1..3).chain([4, 5]) e.to_a #=> [1, 2, 3, 4, 5]
static VALUE enum_chain(int argc, VALUE *argv, VALUE obj) { VALUE enums = rb_ary_new_from_values(1, &obj); rb_ary_cat(enums, argv, argc); return new_enum_chain(enums); }
返回的枚举器中的每个元素都是一个由以下内容组成的 2 元素数组
-
块返回的一个值。
-
一个包含该值返回的元素和块返回相同值的所有后续元素的数组(“块”)
因此
-
与前一个不同的每个块返回值都开始一个新块。
-
与前一个相同的每个块返回值都继续相同的块。
示例
e = (0..10).chunk {|i| (i / 3).floor } # => #<Enumerator: ...> # The enumerator elements. e.next # => [0, [0, 1, 2]] e.next # => [1, [3, 4, 5]] e.next # => [2, [6, 7, 8]] e.next # => [3, [9, 10]]
方法 chunk
对于已经排序的可枚举对象特别有用。此示例统计一个大型单词数组中每个首字母的单词
# Get sorted words from a web page. url = 'https://raw.githubusercontent.com/eneko/data-repository/master/data/words.txt' words = URI::open(url).readlines # Make chunks, one for each letter. e = words.chunk {|word| word.upcase[0] } # => #<Enumerator: ...> # Display 'A' through 'F'. e.each {|c, words| p [c, words.length]; break if c == 'F' }
输出
["A", 17096] ["B", 11070] ["C", 19901] ["D", 10896] ["E", 8736] ["F", 6860]
您可以使用特殊符号 :_alone
将元素强制到其自己的单独块中
a = [0, 0, 1, 1] e = a.chunk{|i| i.even? ? :_alone : true } e.to_a # => [[:_alone, [0]], [:_alone, [0]], [true, [1, 1]]]
例如,您可以将包含 URL 的每行放入其自己的块中
pattern = /http/ open(filename) { |f| f.chunk { |line| line =~ pattern ? :_alone : true }.each { |key, lines| pp lines } }
您可以使用特殊符号 :_separator
或 nil
强制忽略元素(不包含在任何块中)
a = [0, 0, -1, 1, 1] e = a.chunk{|i| i < 0 ? :_separator : true } e.to_a # => [[true, [0, 0]], [true, [1, 1]]]
请注意,分隔符确实结束了块
a = [0, 0, -1, 1, -1, 1] e = a.chunk{|i| i < 0 ? :_separator : true } e.to_a # => [[true, [0, 0]], [true, [1]], [true, [1]]]
例如,可以按如下方式消除 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 }
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, id_chunk_enumerable, enumerable); rb_ivar_set(enumerator, id_chunk_categorize, rb_block_proc()); rb_block_call(enumerator, idInitialize, 0, 0, chunk_i, enumerator); return enumerator; }
为每个分块元素创建一个枚举器。块定义块的开头。
此方法使用接收器枚举器中的相邻元素(elt_before 和 elt_after)拆分每个块。此方法在块返回 false
的 elt_before 和 elt_after 之间拆分块。
块被称为接收器枚举器的长度减一。
结果枚举器将分块元素作为数组生成。因此,可以按如下方式调用 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]]
Enumerable#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, id_slicewhen_enum, enumerable); rb_ivar_set(enumerator, id_slicewhen_pred, pred); rb_ivar_set(enumerator, id_slicewhen_inverted, Qtrue); rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator); return enumerator; }
返回所有非nil
元素的数组
a = [nil, 0, nil, 'a', false, nil, false, nil, 'a', nil, 0, nil] a.compact # => [0, "a", false, false, "a", 0]
static VALUE enum_compact(VALUE obj) { VALUE ary; ary = rb_ary_new(); rb_block_call(obj, id_each, 0, 0, compact_i, ary); return ary; }
返回元素计数,基于参数或块条件(如果给出)。
如果没有参数和块,则返回元素数
[0, 1, 2].count # => 3 {foo: 0, bar: 1, baz: 2}.count # => 3
如果给出了参数object
,则返回等于object
的元素数
[0, 1, 2, 1].count(1) # => 2
如果给出了块,则使用每个元素调用块,并返回块返回真值元素的数目
[0, 1, 2, 3].count {|element| element < 2} # => 2 {foo: 0, bar: 1, baz: 2}.count {|key, value| value < 2} # => 2
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); }
当使用正整数参数n
和块调用时,使用每个元素调用块,然后再次执行,直到执行n
次;返回nil
a = [] (1..4).cycle(3) {|element| a.push(element) } # => nil a # => [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4] a = [] ('a'..'d').cycle(2) {|element| a.push(element) } a # => ["a", "b", "c", "d", "a", "b", "c", "d"] a = [] {foo: 0, bar: 1, baz: 2}.cycle(2) {|element| a.push(element) } a # => [[:foo, 0], [:bar, 1], [:baz, 2], [:foo, 0], [:bar, 1], [:baz, 2]]
如果计数为零或负数,则不调用块。
当使用块调用且n
为nil
时,无限循环。
当不给出块时,返回枚举器
。
static VALUE enum_cycle(int argc, VALUE *argv, VALUE obj) { VALUE ary; VALUE nv = Qnil; long n, i, len; rb_check_arity(argc, 0, 1); RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_cycle_size); if (!argc || NIL_P(nv = argv[0])) { 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; }
对于正整数n
,返回包含除前n
个元素之外的所有元素的数组
r = (1..4) r.drop(3) # => [4] r.drop(2) # => [3, 4] r.drop(1) # => [2, 3, 4] r.drop(0) # => [1, 2, 3, 4] r.drop(50) # => [] h = {foo: 0, bar: 1, baz: 2, bat: 3} h.drop(2) # => [[:baz, 2], [:bat, 3]]
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; }
只要块返回真值,就使用连续元素调用块;返回该点之后所有元素的数组
(1..4).drop_while{|i| i < 3 } # => [3, 4] h = {foo: 0, bar: 1, baz: 2} a = h.drop_while{|element| key, value = *element; value < 2 } a # => [[:baz, 2]]
如果没有给出块,则返回枚举器
。
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; }
使用每个连续重叠n
元组元素调用块;返回self
a = [] (1..5).each_cons(3) {|element| a.push(element) } a # => [[1, 2, 3], [2, 3, 4], [3, 4, 5]] a = [] h = {foo: 0, bar: 1, baz: 2, bam: 3} h.each_cons(2) {|element| a.push(element) } a # => [[[:foo, 0], [:bar, 1]], [[:bar, 1], [:baz, 2]], [[:baz, 2], [:bam, 3]]]
如果没有给出块,则返回枚举器
。
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 obj; 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 obj; }
使用每个元素调用给定的块,将从 yield 中的多个值转换成一个数组;返回 self
a = [] (1..4).each_entry {|element| a.push(element) } # => 1..4 a # => [1, 2, 3, 4] a = [] h = {foo: 0, bar: 1, baz:2} h.each_entry {|element| a.push(element) } # => {:foo=>0, :bar=>1, :baz=>2} a # => [[:foo, 0], [:bar, 1], [:baz, 2]] class Foo include Enumerable def each yield 1 yield 1, 2 yield end end Foo.new.each_entry {|yielded| p yielded }
输出
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; }
使用每个连续的非交集 n
元组元素调用该块;返回 self
a = [] (1..10).each_slice(3) {|tuple| a.push(tuple) } a # => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] a = [] h = {foo: 0, bar: 1, baz: 2, bat: 3, bam: 4} h.each_slice(2) {|tuple| a.push(tuple) } a # => [[[:foo, 0], [:bar, 1]], [[:baz, 2], [:bat, 3]], [[:bam, 4]]]
如果没有给出块,则返回枚举器
。
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 obj; }
如果给出了一个块,则使用每个元素及其索引调用该块;返回 self
h = {} (1..4).each_with_index {|element, i| h[element] = i } # => 1..4 h # => {1=>0, 2=>1, 3=>2, 4=>3} h = {} %w[a b c d].each_with_index {|element, i| h[element] = i } # => ["a", "b", "c", "d"] h # => {"a"=>0, "b"=>1, "c"=>2, "d"=>3} a = [] h = {foo: 0, bar: 1, baz: 2} h.each_with_index {|element, i| a.push([i, element]) } # => {:foo=>0, :bar=>1, :baz=>2} a # => [[0, [:foo, 0]], [1, [:bar, 1]], [2, [:baz, 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; }
对每个元素调用该块一次,同时传递该元素和给定的对象
(1..4).each_with_object([]) {|i, a| a.push(i**2) } # => [1, 4, 9, 16] {foo: 0, bar: 1, baz: 2}.each_with_object({}) {|(k, v), h| h[v] = k } # => {0=>:foo, 1=>:bar, 2=>:baz}
如果没有给出块,则返回枚举器
。
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; }
返回一个包含该块返回的真值元素的数组。
如果给出了一个块,则使用连续的元素调用该块;返回一个包含该块返回的每个真值元素的数组
(0..9).filter_map {|i| i * 2 if i.even? } # => [0, 4, 8, 12, 16] {foo: 0, bar: 1, baz: 2}.filter_map {|key, value| key if value.even? } # => [:foo, :baz]
如果没有给出块,则返回一个 Enumerator
。
static VALUE enum_filter_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, filter_map_i, ary); return ary; }
返回该块返回真值元素的第一个元素。
如果给出了一个块,则使用集合的连续元素调用该块;返回该块返回真值元素的第一个元素
(0..9).find {|element| element > 2} # => 3
如果没有找到这样的元素,则调用 if_none_proc
并返回其返回值。
(0..9).find(proc {false}) {|element| element > 12} # => false {foo: 0, bar: 1, baz: 2}.find {|key, value| key.start_with?('b') } # => [:bar, 1] {foo: 0, bar: 1, baz: 2}.find(proc {[]}) {|key, value| key.start_with?('c') } # => []
如果没有给出块,则返回枚举器
。
static VALUE enum_find(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo; VALUE if_none; if_none = rb_check_arity(argc, 0, 1) ? argv[0] : Qnil; 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; }
返回满足指定条件的第一个元素的索引,如果没有找到这样的元素,则返回 nil
。
给定参数 object
,返回第一个元素的索引,该元素为 ==
object
['a', 'b', 'c', 'b'].find_index('b') # => 1
给定一个块,用连续的元素调用该块;返回块返回真值的第一元素
['a', 'b', 'c', 'b'].find_index {|element| element.start_with?('b') } # => 1 {foo: 0, bar: 1, baz: 2}.find_index {|key, value| value > 1 } # => 2
如果没有给定参数和块,则返回一个 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; }
返回第一个元素或元素。
如果没有参数,则返回第一个元素,如果没有,则返回 nil
(1..4).first # => 1 %w[a b c].first # => "a" {foo: 1, bar: 1, baz: 2}.first # => [:foo, 1] [].first # => nil
如果整数参数为 n
,则返回一个包含存在的第一个 n
个元素的数组
(1..4).first(2) # => [1, 2] %w[a b c d].first(3) # => ["a", "b", "c"] %w[a b c d].first(50) # => ["a", "b", "c", "d"] {foo: 1, bar: 1, baz: 2}.first(2) # => [[:foo, 1], [:bar, 1]] [].first(2) # => []
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; } }
返回块返回的扁平化对象数组。
给定一个块,用连续的元素调用该块;返回块返回的对象的扁平化数组
[0, 1, 2, 3].flat_map {|element| -element } # => [0, -1, -2, -3] [0, 1, 2, 3].flat_map {|element| [element, -element] } # => [0, 0, 1, -1, 2, -2, 3, -3] [[0, 1], [2, 3]].flat_map {|e| e + [100] } # => [0, 1, 100, 2, 3, 100] {foo: 0, bar: 1, baz: 2}.flat_map {|key, value| [key, value] } # => [:foo, 0, :bar, 1, :baz, 2]
如果没有给出块,则返回枚举器
。
别名: collect_concat
。
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; }
返回一个数组,其中包含与给定模式匹配的 self
的基于元素的对象。
如果没有给定块,则返回一个数组,其中包含每个元素,对于这些元素,pattern === element
为 true
a = ['foo', 'bar', 'car', 'moo'] a.grep(/ar/) # => ["bar", "car"] (1..10).grep(3..8) # => [3, 4, 5, 6, 7, 8] ['a', 'b', 0, 1].grep(Integer) # => [0, 1]
如果给定一个块,则用每个匹配元素调用该块,并返回一个数组,其中包含块返回的每个对象
a = ['foo', 'bar', 'car', 'moo'] a.grep(/ar/) {|element| element.upcase } # => ["BAR", "CAR"]
相关: grep_v
。
static VALUE enum_grep(VALUE obj, VALUE pat) { return enum_grep0(obj, pat, Qtrue); }
返回一个数组,其中包含基于 self
的元素的对象,这些元素不与给定模式匹配。
如果没有给定块,则返回一个数组,其中包含每个元素,对于这些元素,pattern === element
为 false
a = ['foo', 'bar', 'car', 'moo'] a.grep_v(/ar/) # => ["foo", "moo"] (1..10).grep_v(3..8) # => [1, 2, 9, 10] ['a', 'b', 0, 1].grep_v(Integer) # => ["a", "b"]
如果给定一个块,则用每个不匹配元素调用该块,并返回一个数组,其中包含块返回的每个对象
a = ['foo', 'bar', 'car', 'moo'] a.grep_v(/ar/) {|element| element.upcase } # => ["FOO", "MOO"]
相关: grep
。
static VALUE enum_grep_v(VALUE obj, VALUE pat) { return enum_grep0(obj, pat, Qfalse); }
如果给定一个块,则返回一个哈希
-
每个键都是块的返回值。
-
每个值都是块返回该键的那些元素的数组。
示例
g = (1..6).group_by {|i| i%3 } g # => {1=>[1, 4], 2=>[2, 5], 0=>[3, 6]} h = {foo: 0, bar: 1, baz: 0, bat: 1} g = h.group_by {|key, value| value } g # => {0=>[[:foo, 0], [:baz, 0]], 1=>[[:bar, 1], [:bat, 1]]}
如果没有给出块,则返回枚举器
。
static VALUE enum_group_by(VALUE obj) { RETURN_SIZED_ENUMERATOR(obj, 0, 0, enum_size); return enum_hashify(obj, 0, 0, group_by_i); }
返回对于任何元素 object == element
是否为 true
(1..4).include?(2) # => true (1..4).include?(5) # => false (1..4).include?('2') # => false %w[a b c d].include?('b') # => true %w[a b c d].include?('2') # => false {foo: 0, bar: 1, baz: 2}.include?(:foo) # => true {foo: 0, bar: 1, baz: 2}.include?('foo') # => false {foo: 0, bar: 1, baz: 2}.include?(0) # => false
通过以下方式从操作数中返回一个对象
-
由
symbol
命名的某个方法。 -
一个块,每个操作数都传递给该块。
对于方法名参数 symbol
,使用该方法组合操作数
# Sum, without initial_operand. (1..4).inject(:+) # => 10 # Sum, with initial_operand. (1..4).inject(10, :+) # => 20
对于一个块,将每个操作数传递给该块
# Sum of squares, without initial_operand. (1..4).inject {|sum, n| sum + n*n } # => 30 # Sum of squares, with initial_operand. (1..4).inject(2) {|sum, n| sum + n*n } # => 32
操作数
如果未给出参数 initial_operand
,则 inject
的操作数只是 self
的元素。示例调用及其操作数
(1..4).inject(:+)
-
[1, 2, 3, 4]
.
(1...4).inject(:+)
-
[1, 2, 3]
.
('a'..'d').inject(:+)
-
['a', 'b', 'c', 'd']
.
('a'...'d').inject(:+)
-
['a', 'b', 'c']
.
具有各种类型的第一个操作数(即 self.first
)的示例
# Integer. (1..4).inject(:+) # => 10 # Float. [1.0, 2, 3, 4].inject(:+) # => 10.0 # Character. ('a'..'d').inject(:+) # => "abcd" # Complex. [Complex(1, 2), 3, 4].inject(:+) # => (8+2i)
如果给出了参数 initial_operand
,则 inject
的操作数是该值加上 self
的元素。示例调用其操作数
(1..4).inject(10, :+)
-
[10, 1, 2, 3, 4]
.
(1...4).inject(10, :+)
-
[10, 1, 2, 3]
.
('a'..'d').inject('e', :+)
-
['e', 'a', 'b', 'c', 'd']
.
('a'...'d').inject('e', :+)
-
['e', 'a', 'b', 'c']
.
具有各种类型的 initial_operand
的示例
# Integer. (1..4).inject(2, :+) # => 12 # Float. (1..4).inject(2.0, :+) # => 12.0 # String. ('a'..'d').inject('foo', :+) # => "fooabcd" # Array. %w[a b c].inject(['x'], :push) # => ["x", "a", "b", "c"] # Complex. (1..4).inject(Complex(2, 2), :+) # => (12+2i)
通过给定方法进行组合
如果给出了方法名参数 symbol
,则操作数将通过该方法进行组合
-
第一个和第二个操作数组合。
-
该结果与第三个操作数组合。
-
该结果与第四个操作数组合。
-
依此类推。
inject
的返回值是最后一次组合的结果。
对 inject
的此调用计算操作数的总和
(1..4).inject(:+) # => 10
具有各种方法的示例
# Integer addition. (1..4).inject(:+) # => 10 # Integer multiplication. (1..4).inject(:*) # => 24 # Character range concatenation. ('a'..'d').inject('', :+) # => "abcd" # String array concatenation. %w[foo bar baz].inject('', :+) # => "foobarbaz" # Hash update. h = [{foo: 0, bar: 1}, {baz: 2}, {bat: 3}].inject(:update) h # => {:foo=>0, :bar=>1, :baz=>2, :bat=>3} # Hash conversion to nested arrays. h = {foo: 0, bar: 1}.inject([], :push) h # => [[:foo, 0], [:bar, 1]]
通过给定块进行组合
如果给出了一个块,则操作数将传递给该块
-
第一个调用传递第一个和第二个操作数。
-
第二个调用传递第一个调用的结果,以及第三个操作数。
-
第三个调用传递第二个调用的结果,以及第四个操作数。
-
依此类推。
inject
的返回值是最后一个块调用的返回值。
对 inject
的此调用提供一个块,该块写入备忘录和元素,并且还对元素求和
(1..4).inject do |memo, element| p "Memo: #{memo}; element: #{element}" memo + element end # => 10
输出
"Memo: 1; element: 2" "Memo: 3; element: 3" "Memo: 6; element: 4"
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; int num_args; if (rb_block_given_p()) { num_args = rb_scan_args(argc, argv, "02", &init, &op); } else { num_args = rb_scan_args(argc, argv, "11", &init, &op); } switch (num_args) { 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 (UNDEF_P(memo->v1)) return Qnil; return memo->v1; }
返回一个 Enumerator::Lazy
,它重新定义大多数 Enumerable
方法,以推迟枚举,并且仅在需要时枚举值。
示例¶ ↑
以下程序查找勾股数
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, rb_keyword_given_p()); /* Qfalse indicates that the Enumerator::Lazy has no method name */ rb_ivar_set(result, id_method, Qfalse); return result; }
返回块返回的对象数组。
如果给定块,则使用连续元素调用该块;返回块返回的对象数组
(0..4).map {|i| i*i } # => [0, 1, 4, 9, 16] {foo: 0, bar: 1, baz: 2}.map {|key, value| value*2} # => [0, 2, 4]
如果没有给出块,则返回枚举器
。
根据给定条件返回具有最大元素的元素。相等元素的顺序是不确定的,并且可能不稳定。
如果没有参数且没有块,则返回最大元素,使用元素自己的方法 <=>
进行比较
(1..4).max # => 4 (-4..-1).max # => -1 %w[d c b a].max # => "d" {foo: 0, bar: 1, baz: 2}.max # => [:foo, 0] [].max # => nil
如果给定正整数参数 n
且没有块,则返回一个数组,其中包含存在的第一个 n
个最大元素
(1..4).max(2) # => [4, 3] (-4..-1).max(2) # => [-1, -2] %w[d c b a].max(2) # => ["d", "c"] {foo: 0, bar: 1, baz: 2}.max(2) # => [[:foo, 0], [:baz, 2]] [].max(2) # => []
如果给定块,则块确定最大元素。该块使用两个元素 a
和 b
调用,并且必须返回
-
如果
a < b
,则返回负整数。 -
如果
a == b
,则返回零。 -
如果
a > b
,则返回正整数。
如果给定块且没有参数,则返回由块确定的最大元素
%w[xxx x xxxx xx].max {|a, b| a.size <=> b.size } # => "xxxx" h = {foo: 0, bar: 1, baz: 2} h.max {|pair1, pair2| pair1[1] <=> pair2[1] } # => [:baz, 2] [].max {|a, b| a <=> b } # => nil
如果给定块和正整数参数 n
,则返回一个数组,其中包含由块确定的第一个 n
个最大元素。
%w[xxx x xxxx xx].max(2) {|a, b| a.size <=> b.size } # => ["xxxx", "xxx"] h = {foo: 0, bar: 1, baz: 2} h.max(2) {|pair1, pair2| pair1[1] <=> pair2[1] } # => [[:baz, 2], [:bar, 1]] [].max(2) {|a, b| a <=> b } # => []
static VALUE enum_max(int argc, VALUE *argv, VALUE obj) { VALUE memo; struct max_t *m = NEW_MEMO_FOR(struct max_t, memo); VALUE result; VALUE num; if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0])) return rb_nmin_run(obj, num, 0, 1, 0); m->max = Qundef; 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 (UNDEF_P(result)) return Qnil; return result; }
返回块返回最大值的元素。
给定一个块且没有参数时,返回块返回最大值的元素
(1..4).max_by {|element| -element } # => 1 %w[a b c d].max_by {|element| -element.ord } # => "a" {foo: 0, bar: 1, baz: 2}.max_by {|key, value| -value } # => [:foo, 0] [].max_by {|element| -element } # => nil
给定一个块和正整数参数 n
时,返回一个包含 n
个块返回最大值的元素的数组
(1..4).max_by(2) {|element| -element } # => [1, 2] %w[a b c d].max_by(2) {|element| -element.ord } # => ["a", "b"] {foo: 0, bar: 1, baz: 2}.max_by(2) {|key, value| -value } # => [[:foo, 0], [:bar, 1]] [].max_by(2) {|element| -element } # => []
如果没有给定块,则返回一个 Enumerator
。
static VALUE enum_max_by(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo; VALUE num; rb_check_arity(argc, 0, 1); RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); if (argc && !NIL_P(num = argv[0])) 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; }
根据给定的条件返回元素中具有最小元素的元素。相等元素的顺序是不确定的,并且可能不稳定。
如果没有参数和块,则使用元素自己的方法 <=>
进行比较,返回最小元素
(1..4).min # => 1 (-4..-1).min # => -4 %w[d c b a].min # => "a" {foo: 0, bar: 1, baz: 2}.min # => [:bar, 1] [].min # => nil
给定正整数参数 n
且没有块时,返回一个包含存在的第一个 n
个最小元素的数组
(1..4).min(2) # => [1, 2] (-4..-1).min(2) # => [-4, -3] %w[d c b a].min(2) # => ["a", "b"] {foo: 0, bar: 1, baz: 2}.min(2) # => [[:bar, 1], [:baz, 2]] [].min(2) # => []
给定一个块时,该块确定最小元素。该块使用两个元素 a
和 b
调用,并且必须返回
-
如果
a < b
,则返回负整数。 -
如果
a == b
,则返回零。 -
如果
a > b
,则返回正整数。
给定一个块且没有参数时,返回由块确定的最小元素
%w[xxx x xxxx xx].min {|a, b| a.size <=> b.size } # => "x" h = {foo: 0, bar: 1, baz: 2} h.min {|pair1, pair2| pair1[1] <=> pair2[1] } # => [:foo, 0] [].min {|a, b| a <=> b } # => nil
给定一个块和正整数参数 n
时,返回一个包含由块确定的第一个 n
个最小元素的数组。
%w[xxx x xxxx xx].min(2) {|a, b| a.size <=> b.size } # => ["x", "xx"] h = {foo: 0, bar: 1, baz: 2} h.min(2) {|pair1, pair2| pair1[1] <=> pair2[1] } # => [[:foo, 0], [:bar, 1]] [].min(2) {|a, b| a <=> b } # => []
static VALUE enum_min(int argc, VALUE *argv, VALUE obj) { VALUE memo; struct min_t *m = NEW_MEMO_FOR(struct min_t, memo); VALUE result; VALUE num; if (rb_check_arity(argc, 0, 1) && !NIL_P(num = argv[0])) return rb_nmin_run(obj, num, 0, 0, 0); m->min = Qundef; 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 (UNDEF_P(result)) return Qnil; return result; }
返回块返回最小值的元素。
给定一个块且没有参数时,返回块返回最小值的元素
(1..4).min_by {|element| -element } # => 4 %w[a b c d].min_by {|element| -element.ord } # => "d" {foo: 0, bar: 1, baz: 2}.min_by {|key, value| -value } # => [:baz, 2] [].min_by {|element| -element } # => nil
给定一个块和正整数参数 n
时,返回一个包含 n
个块返回最小值的元素的数组
(1..4).min_by(2) {|element| -element } # => [4, 3] %w[a b c d].min_by(2) {|element| -element.ord } # => ["d", "c"] {foo: 0, bar: 1, baz: 2}.min_by(2) {|key, value| -value } # => [[:baz, 2], [:bar, 1]] [].min_by(2) {|element| -element } # => []
如果没有给定块,则返回一个 Enumerator
。
static VALUE enum_min_by(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo; VALUE num; rb_check_arity(argc, 0, 1); RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); if (argc && !NIL_P(num = argv[0])) 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; }
根据给定的条件返回包含最小值和最大值的 2 元素数组。相等元素的顺序是不确定的,并且可能不稳定。
如果没有参数和块,则使用元素自己的方法 <=>
进行比较,返回最小值和最大值元素
(1..4).minmax # => [1, 4] (-4..-1).minmax # => [-4, -1] %w[d c b a].minmax # => ["a", "d"] {foo: 0, bar: 1, baz: 2}.minmax # => [[:bar, 1], [:foo, 0]] [].minmax # => [nil, nil]
如果给出了一个块,则返回由块确定的最小值和最大值元素
%w[xxx x xxxx xx].minmax {|a, b| a.size <=> b.size } # => ["x", "xxxx"] h = {foo: 0, bar: 1, baz: 2} h.minmax {|pair1, pair2| pair1[1] <=> pair2[1] } # => [[:foo, 0], [:baz, 2]] [].minmax {|a, b| a <=> b } # => [nil, nil]
static VALUE enum_minmax(VALUE obj) { VALUE memo; struct minmax_t *m = NEW_MEMO_FOR(struct minmax_t, memo); m->min = Qundef; m->last = Qundef; if (rb_block_given_p()) { rb_block_call(obj, id_each, 0, 0, minmax_ii, memo); if (!UNDEF_P(m->last)) minmax_ii_update(m->last, m->last, m); } else { rb_block_call(obj, id_each, 0, 0, minmax_i, memo); if (!UNDEF_P(m->last)) minmax_i_update(m->last, m->last, m); } if (!UNDEF_P(m->min)) { return rb_assoc_new(m->min, m->max); } return rb_assoc_new(Qnil, Qnil); }
返回一个 2 元素数组,其中包含块返回最小值和最大值的元素
(1..4).minmax_by {|element| -element } # => [4, 1] %w[a b c d].minmax_by {|element| -element.ord } # => ["d", "a"] {foo: 0, bar: 1, baz: 2}.minmax_by {|key, value| -value } # => [[:baz, 2], [:foo, 0]] [].minmax_by {|element| -element } # => [nil, nil]
如果没有给定块,则返回一个 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 (!UNDEF_P(m->last_bv)) 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); }
返回是否没有元素满足给定的条件。
如果没有参数和块,则返回是否没有元素为真
(1..4).none? # => false [nil, false].none? # => true {foo: 0}.none? # => false {foo: 0, bar: 1}.none? # => false [].none? # => true
如果给出了参数 pattern
且没有块,则返回对于没有元素 element
,pattern === element
[nil, false, 1.1].none?(Integer) # => true %w[bar baz bat bam].none?(/m/) # => false %w[bar baz bat bam].none?(/foo/) # => true %w[bar baz bat bam].none?('ba') # => true {foo: 0, bar: 1, baz: 2}.none?(Hash) # => true {foo: 0}.none?(Array) # => false [].none?(Integer) # => true
如果给出了一个块,则返回块是否为没有元素返回真值
(1..4).none? {|element| element < 1 } # => true (1..4).none? {|element| element < 2 } # => false {foo: 0, bar: 1, baz: 2}.none? {|key, value| value < 0 } # => true {foo: 0, bar: 1, baz: 2}.none? {|key, value| value < 1 } # => false
static VALUE enum_none(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo = MEMO_ENUM_NEW(Qtrue); WARN_UNUSED_BLOCK(argc); rb_block_call(obj, id_each, 0, 0, ENUMFUNC(none), (VALUE)memo); return memo->v1; }
返回是否正好有一个元素满足给定的条件。
如果没有参数和块,则返回是否正好有一个元素为真
(1..1).one? # => true [1, nil, false].one? # => true (1..4).one? # => false {foo: 0}.one? # => true {foo: 0, bar: 1}.one? # => false [].one? # => false
带参数 pattern
且没有块时,返回是否对于恰好一个元素 element
,pattern === element
[nil, false, 0].one?(Integer) # => true [nil, false, 0].one?(Numeric) # => true [nil, false, 0].one?(Float) # => false %w[bar baz bat bam].one?(/m/) # => true %w[bar baz bat bam].one?(/foo/) # => false %w[bar baz bat bam].one?('ba') # => false {foo: 0, bar: 1, baz: 2}.one?(Array) # => false {foo: 0}.one?(Array) # => true [].one?(Integer) # => false
如果给定一个块,则返回该块是否为恰好一个元素返回真值
(1..4).one? {|element| element < 2 } # => true (1..4).one? {|element| element < 1 } # => false {foo: 0, bar: 1, baz: 2}.one? {|key, value| value < 1 } # => true {foo: 0, bar: 1, baz: 2}.one? {|key, value| value < 2 } # => false
static VALUE enum_one(int argc, VALUE *argv, VALUE obj) { struct MEMO *memo = MEMO_ENUM_NEW(Qundef); VALUE result; WARN_UNUSED_BLOCK(argc); rb_block_call(obj, id_each, 0, 0, ENUMFUNC(one), (VALUE)memo); result = memo->v1; if (UNDEF_P(result)) return Qfalse; return result; }
如果给定一个块,则返回两个数组的数组
-
第一个数组包含块返回真值元素。
-
另一个数组包含所有其他元素。
示例
p = (1..4).partition {|i| i.even? } p # => [[2, 4], [1, 3]] p = ('a'..'d').partition {|c| c < 'c' } p # => [["a", "b"], ["c", "d"]] h = {foo: 0, bar: 1, baz: 2, bat: 3} p = h.partition {|key, value| key.start_with?('b') } p # => [[[:bar, 1], [:baz, 2], [:bat, 3]], [[:foo, 0]]] p = h.partition {|key, value| value < 2 } p # => [[[:foo, 0], [:bar, 1]], [[:baz, 2], [:bat, 3]]]
如果没有给出块,则返回枚举器
。
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); }
返回块拒绝的对象数组。
如果给定一个块,则使用连续元素调用该块;返回块返回 nil
或 false
的那些元素的数组
(0..9).reject {|i| i * 2 if i.even? } # => [1, 3, 5, 7, 9] {foo: 0, bar: 1, baz: 2}.reject {|key, value| key if value.odd? } # => {:foo=>0, :baz=>2}
如果没有给出块,则返回一个 Enumerator
。
相关:select
。
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; }
如果给定一个块,则使用每个元素调用该块,但以相反的顺序;返回 self
a = [] (1..4).reverse_each {|element| a.push(-element) } # => 1..4 a # => [-4, -3, -2, -1] a = [] %w[a b c d].reverse_each {|element| a.push(element) } # => ["a", "b", "c", "d"] a # => ["d", "c", "b", "a"] a = [] h.reverse_each {|element| a.push(element) } # => {:foo=>0, :bar=>1, :baz=>2} a # => [[:baz, 2], [:bar, 1], [:foo, 0]]
如果没有给出块,则返回枚举器
。
static VALUE enum_reverse_each(int argc, VALUE *argv, VALUE obj) { VALUE ary; long len; RETURN_SIZED_ENUMERATOR(obj, argc, argv, enum_size); ary = enum_to_a(argc, argv, obj); len = RARRAY_LEN(ary); while (len--) { long nlen; rb_yield(RARRAY_AREF(ary, len)); nlen = RARRAY_LEN(ary); if (nlen < len) { len = nlen; } } return obj; }
返回包含块选择的元素的数组。
如果给定一个块,则使用连续元素调用该块;返回块返回真值元素的数组
(0..9).select {|element| element % 3 == 0 } # => [0, 3, 6, 9] a = {foo: 0, bar: 1, baz: 2}.select {|key, value| key.start_with?('b') } a # => {:bar=>1, :baz=>2}
如果没有给出块,则返回枚举器
。
相关:reject
。
为每个块状元素创建一个枚举器。块的末尾由pattern和块定义。
如果 pattern === elt
返回 true
或块为元素返回 true
,则该元素是块的末尾。
从enum的第一个元素到最后一个元素调用 ===
和block。
结果枚举器将分块元素作为数组生成。因此,可以按如下方式调用 each
方法
enum.slice_after(pattern).each { |ary| ... } enum.slice_after { |elt| bool }.each { |ary| ... }
Enumerator
类和其他方法,例如 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, id_sliceafter_enum, enumerable); rb_ivar_set(enumerator, id_sliceafter_pat, pat); rb_ivar_set(enumerator, id_sliceafter_pred, pred); rb_block_call(enumerator, idInitialize, 0, 0, sliceafter_i, enumerator); return enumerator; }
使用参数 pattern
,返回一个使用该模式将元素划分为数组(“切片”)的枚举器。如果 element === pattern
(或它是第一个元素),则元素开始一个新切片。
a = %w[foo bar fop for baz fob fog bam foy] e = a.slice_before(/ba/) # => #<Enumerator: ...> e.each {|array| p array }
输出
["foo"] ["bar", "fop", "for"] ["baz", "fob", "fog"] ["bam", "foy"]
使用一个块,返回一个使用该块将元素划分为数组的枚举器。如果其块返回为真值(或它是第一个元素),则元素开始一个新切片
e = (1..20).slice_before {|i| i % 4 == 2 } # => #<Enumerator: ...> e.each {|array| p array }
输出
[1] [2, 3, 4, 5] [6, 7, 8, 9] [10, 11, 12, 13] [14, 15, 16, 17] [18, 19, 20]
还可以使用 Enumerator
类和 Enumerable
模块的其他方法,例如 to_a
、map
等。
例如,可以按如下方式实现对变更日志条目的迭代
# 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 行开头的系列邮件。因此,可以通过在 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 } }
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, id_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, id_slicebefore_sep_pat, sep_pat); } rb_ivar_set(enumerator, id_slicebefore_enumerable, enumerable); rb_block_call(enumerator, idInitialize, 0, 0, slicebefore_i, enumerator); return enumerator; }
为每个分块元素创建一个枚举器。块定义块的开头。
此方法使用接收者枚举器中的相邻元素(elt_before 和 elt_after)拆分每个块。此方法拆分块,在块返回 true
的 elt_before 和 elt_after 之间。
块被称为接收器枚举器的长度减一。
结果枚举器将分块元素作为数组生成。因此,可以按如下方式调用 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]]
可以按如下方式对段落(带有尾随空行的非空行)进行分块:(请参阅 Enumerable.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"]]
Enumerable.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, id_slicewhen_enum, enumerable); rb_ivar_set(enumerator, id_slicewhen_pred, pred); rb_ivar_set(enumerator, id_slicewhen_inverted, Qfalse); rb_block_call(enumerator, idInitialize, 0, 0, slicewhen_i, enumerator); return enumerator; }
返回一个包含 self
的已排序元素的数组。相等元素的顺序是不确定的,并且可能不稳定。
如果没有给定块,则排序使用元素自己的方法 <=>
进行比较
%w[b c a d].sort # => ["a", "b", "c", "d"] {foo: 0, bar: 1, baz: 2}.sort # => [[:bar, 1], [:baz, 2], [:foo, 0]]
如果给定一个块,则块中的比较确定顺序。该块使用两个元素 a
和 b
调用,并且必须返回
-
如果
a < b
,则返回负整数。 -
如果
a == b
,则返回零。 -
如果
a > b
,则返回正整数。
示例
a = %w[b c a d] a.sort {|a, b| b <=> a } # => ["d", "c", "b", "a"] h = {foo: 0, bar: 1, baz: 2} h.sort {|a, b| b <=> a } # => [[:foo, 0], [:baz, 2], [:bar, 1]]
另请参见 sort_by
。它实现了一个 Schwartzian 变换,当键计算或比较开销较大时很有用。
static VALUE enum_sort(VALUE obj) { return rb_ary_sort_bang(enum_to_a(0, 0, obj)); }
如果给定一个块,则返回一个 self
元素数组,该数组根据每个元素的块返回值进行排序。相等元素的顺序是不确定的,并且可能是不可靠的。
示例
a = %w[xx xxx x xxxx] a.sort_by {|s| s.size } # => ["x", "xx", "xxx", "xxxx"] a.sort_by {|s| -s.size } # => ["xxxx", "xxx", "xx", "x"] h = {foo: 2, bar: 1, baz: 0} h.sort_by{|key, value| value } # => [[:baz, 0], [:bar, 1], [:foo, 2]] h.sort_by{|key, value| key } # => [[:bar, 1], [:baz, 0], [:foo, 2]]
如果没有给出块,则返回枚举器
。
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 用户通常将这种方法称为 Schwartzian 变换,以 Randal Schwartz 命名。我们构造一个临时数组,其中每个元素都是一个数组,其中包含我们的排序键和文件名。我们对该数组进行排序,然后从结果中提取文件名。
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"]
要生成特定顺序的逆序,可以使用以下内容
ary.sort_by { ... }.reverse!
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_hidden_new(SORT_BY_BUFSIZE*2); rb_ary_store(buf, SORT_BY_BUFSIZE*2-1, Qnil); memo = MEMO_NEW(0, 0, 0); data = (struct sort_by_data *)&memo->v1; RB_OBJ_WRITE(memo, &data->ary, ary); RB_OBJ_WRITE(memo, &data->buf, buf); data->n = 0; data->primitive_uniformed = SORT_BY_UNIFORMED((CMP_OPTIMIZABLE(FLOAT) && CMP_OPTIMIZABLE(INTEGER)), CMP_OPTIMIZABLE(FLOAT), CMP_OPTIMIZABLE(INTEGER)); 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) { if (data->primitive_uniformed) { RARRAY_PTR_USE(ary, ptr, rb_uniform_intro_sort_2((struct rb_uniform_sort_data*)ptr, (struct rb_uniform_sort_data*)(ptr + RARRAY_LEN(ary)))); } else { 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); return ary; }
如果没有给定块,则返回 initial_value
和元素的总和
(1..100).sum # => 5050 (1..100).sum(1) # => 5051 ('a'..'d').sum('foo') # => "fooabcd"
通常,总和使用 +
和 each
方法计算;为了性能优化,这些方法可能不会被使用,因此对这些方法的任何重新定义可能不会在此处生效。
一种这样的优化:在可能的情况下,使用高斯求和公式 n(n+1)/2 计算
100 * (100 + 1) / 2 # => 5050
如果给定一个块,则用每个元素调用该块;返回 initial_value
和块返回值的总和
(1..4).sum {|i| i*i } # => 30 (1..4).sum(100) {|i| i*i } # => 130 h = {a: 0, b: 1, c: 2, d: 3, e: 4, f: 5} h.sum {|key, value| value.odd? ? value : 0 } # => 9 ('a'..'f').sum('x') {|c| c < 'd' ? c : '' } # => "xabc"
static VALUE enum_sum(int argc, VALUE* argv, VALUE obj) { struct enum_sum_memo memo; VALUE beg, end; int excl; memo.v = (rb_check_arity(argc, 0, 1) == 0) ? LONG2FIX(0) : argv[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; } else { memo.f = 0.0; memo.c = 0.0; } if (RTEST(rb_range_values(obj, &beg, &end, &excl))) { if (!memo.block_given && !memo.float_value && (FIXNUM_P(beg) || RB_BIGNUM_TYPE_P(beg)) && (FIXNUM_P(end) || RB_BIGNUM_TYPE_P(end))) { 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 (!UNDEF_P(memo.r)) { memo.v = rb_rational_plus(memo.r, memo.v); } return memo.v; } }
对于非负整数n
,返回前n
个元素
r = (1..4) r.take(2) # => [1, 2] r.take(0) # => [] h = {foo: 0, bar: 1, baz: 2, bat: 3} h.take(2) # => [[:foo, 0], [:bar, 1]]
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; }
只要代码块返回真值,就用连续元素调用该代码块;返回到该点为止的所有元素的数组
(1..4).take_while{|i| i < 3 } # => [1, 2] h = {foo: 0, bar: 1, baz: 2} h.take_while{|element| key, value = *element; value < 2 } # => [[:foo, 0], [:bar, 1]]
如果没有给出块,则返回枚举器
。
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; }
返回包含相等元素计数的哈希
-
每个键都是
self
的元素。 -
每个值都等于该键的元素数。
没有参数
%w[a b c b c a c b].tally # => {"a"=>2, "b"=>3, "c"=>3}
使用哈希参数时,该哈希用于计数(而不是新哈希),并返回;这对于累积多个枚举器的计数可能很有用
hash = {} hash = %w[a c d b c a].tally(hash) hash # => {"a"=>2, "c"=>2, "d"=>1, "b"=>1} hash = %w[b a z].tally(hash) hash # => {"a"=>3, "c"=>2, "d"=>1, "b"=>2, "z"=>1} hash = %w[b a m].tally(hash) hash # => {"a"=>4, "c"=>2, "d"=>1, "b"=>3, "z"=>1, "m"=> 1}
static VALUE enum_tally(int argc, VALUE *argv, VALUE obj) { VALUE hash; if (rb_check_arity(argc, 0, 1)) { hash = rb_to_hash_type(argv[0]); rb_check_frozen(hash); } else { hash = rb_hash_new(); } return enum_hashify_into(obj, 0, 0, tally_i, hash); }
返回包含self
中项的数组
(0..4).to_a # => [0, 1, 2, 3, 4]
static VALUE enum_to_a(int argc, VALUE *argv, VALUE obj) { VALUE ary = rb_ary_new(); rb_block_call_kw(obj, id_each, argc, argv, collect_all, ary, RB_PASS_CALLED_KEYWORDS); return ary; }
当self
由 2 元素数组组成时,返回一个哈希,其每个条目都是由其中一个数组形成的键值对
[[:foo, 0], [:bar, 1], [:baz, 2]].to_h # => {:foo=>0, :bar=>1, :baz=>2}
当给定一个代码块时,使用self
的每个元素调用该代码块;该代码块应返回一个 2 元素数组,该数组成为返回的哈希中的键值对
(0..3).to_h {|i| [i, i ** 2]} # => {0=>0, 1=>1, 2=>4, 3=>9}
如果self
的元素不是 2 元素数组,并且没有传递代码块,则引发异常。
static VALUE enum_to_h(int argc, VALUE *argv, VALUE obj) { rb_block_call_func *iter = rb_block_given_p() ? enum_to_h_ii : enum_to_h_i; return enum_hashify(obj, argc, argv, iter); }
使用给定参数从枚举对象生成一个集合。需要require "set"
才能使用此方法。
# File lib/set.rb, line 847 def to_set(klass = Set, *args, &block) klass.new(self, *args, &block) end
如果没有代码块,则返回一个仅包含唯一元素的新数组;该数组没有两个元素e0
和e1
,使得e0.eql?(e1)
%w[a b c c b a a b c].uniq # => ["a", "b", "c"] [0, 1, 2, 2, 1, 0, 0, 1, 2].uniq # => [0, 1, 2]
使用代码块时,返回一个新数组,其中仅包含代码块返回唯一值的元素
a = [0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1] a.uniq {|i| i.even? ? i : 0 } # => [0, 2, 4] a = %w[a b c d e e d c b a a b c d e] a.uniq {|c| c < 'c' } # => ["a", "c"]
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; }
如果没有给定代码块,则返回一个新数组new_array
,其大小为 self.size,其元素为数组。每个嵌套数组new_array[n]
的大小为other_enums.size+1
,并且包含
-
self 的第
n
个元素。 -
每个
other_enums
的第n
个元素。
如果所有 other_enums
和 self 大小相同,则所有元素都包含在结果中,并且没有 nil
填充
a = [:a0, :a1, :a2, :a3] b = [:b0, :b1, :b2, :b3] c = [:c0, :c1, :c2, :c3] d = a.zip(b, c) d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]] f = {foo: 0, bar: 1, baz: 2} g = {goo: 3, gar: 4, gaz: 5} h = {hoo: 6, har: 7, haz: 8} d = f.zip(g, h) d # => [ # [[:foo, 0], [:goo, 3], [:hoo, 6]], # [[:bar, 1], [:gar, 4], [:har, 7]], # [[:baz, 2], [:gaz, 5], [:haz, 8]] # ]
如果 other_enums 中的任何可枚举对象小于 self,则使用 nil
填充至 self.size
a = [:a0, :a1, :a2, :a3] b = [:b0, :b1, :b2] c = [:c0, :c1] d = a.zip(b, c) d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, nil], [:a3, nil, nil]]
如果 other_enums 中的任何可枚举对象大于 self,则忽略其尾部元素
a = [:a0, :a1, :a2, :a3] b = [:b0, :b1, :b2, :b3, :b4] c = [:c0, :c1, :c2, :c3, :c4, :c5] d = a.zip(b, c) d # => [[:a0, :b0, :c0], [:a1, :b1, :c1], [:a2, :b2, :c2], [:a3, :b3, :c3]]
当给定一个块时,使用每个子数组(如上所示形成)调用该块;返回 nil
a = [:a0, :a1, :a2, :a3] b = [:b0, :b1, :b2, :b3] c = [:c0, :c1, :c2, :c3] a.zip(b, c) {|sub_array| p sub_array} # => nil
输出
[:a0, :b0, :c0] [:a1, :b1, :c1] [:a2, :b2, :c2] [:a3, :b3, :c3]
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; }