Ruby Enumerable模块

629 查看

Enumerable和Comparable是Ruby提供的非常重要的模块。这一篇文章主要介绍Enumerable的一些方法。
Enumerable模块提供了遍历,搜索,排序等方法。如果我们自定义的类需要实现这些方法,必须实现一个each方法。如果需要使用max,min,sort等方法,因为这些方法是集合的元素之间的排序,所以该类还必须使用<=>方法。

实例方法

1.all?[{|obj| block}] -> true of false

%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

2.any?[{|obj| block}] -> true of false

%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

3.chunk{|elt| ...} -> an_enumerator OR chunk(initial_state){|elt,state|...} -> an_enumerator
从文档可以看出:该方法将结果返回一个Enumerator实例。这种情况在Enumerable模块中经常可以看到,那么我们就可以完整的使用Enumerator类的方法,由于Enumerator类又继承了Enumerable类的所有方法,所以可以看到对于返回的结果进行了扩充。
下面列出Enumerator的实例方法,在下一篇文章在详细的介绍Enumerator。
#each,#each_with_index,#each_with_object,#feed,#inspect,#next,#next_values,#peek,#peek_values,#rewind,#size,#with_index,#with_object
栗子:

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

可以看出来chunk方法返回的Enumerator对象的元素是由(block处理的结果,[元素])组成的

require 'pp'

[1,2,3,4,5,6,7,8].chunk do |n|
  n+1
end.each do |n,arr|
  pp [n,arr]
end

返回的结果如下:

[2, [1]]
[3, [2]]
[4, [3]]
[5, [4]]
[6, [5]]
[7, [6]]
[8, [7]]
[9, [8]]

此方法非常适合将元素排序,下面的栗子计算了单词的每个开头字母

open('words') do |f|
  #首先我们在block中返回words文件中的所有行,没有对其进行任何处理
  #输出的结果:是每行的开头字母,以及每行的长度。
  f.chunk { |line| line }.each { |ch,line| pp [ch.chr, line[0].length] }
end

4.collect{|obj|block} OR collect -> an_enumerator

(1..4).collect { |i| i*i }  #=> [1, 4, 9, 16]
(1..4).collect { "cat"  }   #=> ["cat", "cat", "cat", "cat"]

5.collect_concat{|obj| block} -> array OR 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]

别名就是flat_map

6.count -> int OR count(item) -> int OR count{ |obj| block } -> int
返回集合的数量,如果传递了参数,返回集合中和参数相同元素的个数,如果传递了block,返回block中为真的元素个数

ary = [1, 2, 4, 2]
ary.count               #=> 4
ary.count(2)            #=> 2
ary.count{ |x| x%2==0 } #=> 3

7.cycle(n=nil) {|obj| block} -> nil OR cycle(n=nil) -> an_enumerator

a = ["a", "b", "c"]
a.cycle { |x| puts x }  # 打印: a, b, c, a, b, c,...无限多个
a.cycle(2) { |x| puts x }  # 打印:a, b, c, a, b, c.

8.detect(ifnone=nil){|obj| block} -> obj or nil OR detect(ifnone=nil) -> an_enumerator

(1..10).detect  { |i| i % 5 == 0 and i % 7 == 0 }   #=> nil
(1..100).detect { |i| i % 5 == 0 and i % 7 == 0 }   #=> 35

当然如果该方法没有返回值时,可以为该方法指定一个默认值:

class Test
  def default
    'I am default'
  end
end

t = Test.new
pp (1..10).detect(t.method(:default)) { |i| i > 10 }

或者

pp (1..10).detect(lambda { "I am default" }) { |i| i > 10 }

9.drop(n) -> array
删除前n项元素,返回剩余元素

a = [1, 2, 3, 4, 5, 0]
a.drop(3)             #=> [4, 5, 0]

10.drop_while{|arr| block} -> array OR drop_while -> an_enumerator

a = [1, 2, 3, 4, 5, 0]
a.drop_while { |i| i < 3 }   #=> [3, 4, 5, 0]

11.each_cons(n){...} -> nil OR each_cons(n) -> an_enumerator

(1..10).each_cons(3) { |a| p a }
# 结果如下
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]

12.ench_entry{|obj| block} -> enum

%w(1 2 3 4).each_entry {|x| p x}

13.each_slice(n){...} -> nil OR each_slice(n) -> an_enumerator

(1..10).each_slice(3) { |a| p a }
# 结果如下
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
[10]

14.each_with_index(*args){|obj,i| block} -> enum OR each_with_index(*args) -> an_enumerator

hash = Hash.new
%w(cat dog wombat).each_with_index { |item, index|
  hash[item] = index
}
hash   #=> {"cat"=>0, "dog"=>1, "wombat"=>2}

15.each_with_object(obj){|(*args), memo_obj|...} -> obj OR 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]

16.entries -> array

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

17.finddetect
18.find_all{|obj| block} -> array OR find_all -> an_enumerator

(1..10).find_all { |i|  i % 3 == 0 }   #=> [3, 6, 9]
[1,2,3,4,5].select { |num|  num.even?  }   #=> [2, 4]

19.finde_index(value) -> int or nil OR finde_index{|obj| block} -> int or 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

20.first -> obj or nil OR first(n) -> an_array

%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

21.grep(pattern) -> array OR grep(pattern) {|obj| block} -> array

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

22.group_by{|obj| block} -> a_hash OR group_by -> an_enumerator

(1..6).group_by { |i| i%3 }   #=> {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}

23.include?(obj) -> true or false

IO.constants.include? :SEEK_SET          #=> true
IO.constants.include? :SEEK_NO_FURTHER   #=> false

24.inject

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

25.none? [{|obj| block}] -> true or false

%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

26.one? [{|obj| block} -> true or false]

%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

27.partition {|obj| block} -> [true_array, false_array]
分区方法

(1..6).partition { |v| v.even? }  #=> [[2, 4, 6], [1, 3, 5]]