Ruby的迭代器

迭代意味着像循环一样多次执行一次事情,它一个接一个返回数组或哈希里面的元素,本章中会为大家介绍 Ruby 都有哪些迭代器。

1. each

Ruby 的每个迭代器都从哈希和数组中返回每个元素,最常见的是 each 迭代器。

下面是一个数组的例子。

实例:

[1, 2, 3, 4, 5].each do |number|
  puts number
end

# ---- 输出结果 ----
1
2
3
4
5

解释:number 参数代表着每次迭代器循环的每个元素,一共循环 5 次,分别输出 1,2,3,4,5。

上面的场景同样适用于哈希:

实例:

{a: 1, b: 2, c: 3}.each do |item|
  puts "======"
  puts item
  puts item.class
  puts "======"
end

# ---- 输出结果 ----
======
a
1
Array
======
======
b
2
Array
======
======
c
3
Array
======

解释:通过上述例子我们可以看到,我们在每次迭代出来的元素是一个Array,第一个元素是每个键值对的键,第二个元素是每个键值对的值。

1.1 each_key 和 each_value

对于哈希,我们还可以只迭代其值或者键。这里我们有2个方法,一个是each_key,一个是each_value,它们分别迭代哈希的所有键和所有值:

实例:

{a: '1', b: '2', c: '3', d: '4', e: '5'}.each_key do |key|
  puts "key:#{key}"
end

# ---- 输出结果 ----
key:a class: Symbol
key:b class: Symbol
key:c class: Symbol
key:d class: Symbol
key:e class: Symbol

实例:

{a: '1', b: '2', c: '3', d: '4', e: '5'}.each_value do |value|
  puts "value:#{value} class: #{value.class}"
end

# ---- 输出结果 ----
value:1 class: String
value:2 class: String
value:3 class: String
value:4 class: String
value:5 class: String

2. each_with_index

each 的基础上我们还需要打印出每一个元素对应的索引,这样我们可以使用each_with_index

下面是一个数组的例子。

实例:

[1, 2, 3, 4, 5].each_with_index do |number, index|
  puts "index: #{index} nnumber:#{number}"
end

# ---- 输出结果 ----
index: 0 number:1
index: 1 number:2
index: 2 number:3
index: 3 number:4
index: 4 number:5

解释:上述代码我们块中增加了一个index,它代表着每个元素的索引,从0开始依次加1。

下面是一个哈希的例子:

实例:

{a: '1', b: '2', c: '3', d: '4', e: '5'}.each_with_index do |value, index|
  puts "value:#{value} class: #{value.class} index: #{index}"
end

# ---- 输出结果 ----
value:[:a, "1"] class: Array index: 0
value:[:b, "2"] class: Array index: 1
value:[:c, "3"] class: Array index: 2
value:[:d, "4"] class: Array index: 3
value:[:e, "5"] class: Array index: 4

3. 迭代器的返回值

上面我们讲了each,在Ruby中我们类似each的迭代器还有很多,比如:mapcollectinjectrejectselect等,用到这些迭代器的时候,我们主要使用它们的返回值。

让我们以数组[1,2,3,4,5,6,7,8,9,10]举例:

3.1 each

each 可以把数组每个元素作为参数执行操作,但返回值仍是数组本身。

实例:

a = [1,2,3,4,5,6,7,8,9,10].each {|e| puts e.to_s + '!'}
puts a.to_s

# ---- 输出结果 ----
1!
2!
3!
4!
5!
6!
7!
8!
9!
10!
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 

3.2 map

对每个数组元素执行操作,原始数组未修改,返回值是修改之后的数组。

实例:

a = [1,2,3,4,5,6,7,8,9,10].map{|e| e*3 }
puts a.to_s

# ---- 输出结果 ----
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

3.3 collect

collectmap的别名方法,用法同map

3.4 inject

取一个累加器(sum)并更改它的值为与数组中的元素一样多的次数。返回累加器的最终值。

实例:

a = [1,2,3,4,5,6,7,8,9,10].inject{|sum,e| sum += e }
puts a

# ---- 输出结果 ----
55

3.5 reduce

reduceinject的别名,使用方法一样,不过更多用于递减操作。

3.6 select

为每个数组元素运行一个表达式,如果为true,则将该元素添加到返回的输出中。在其他语言中,这称为过滤器。

实例:

a = [1,2,3,4,5,6,7,8,9,10].select{|el| el%2 == 0 }
puts a.to_s

# ---- 输出结果 ----
[2,4,6,8,10]

3.7 find

取得一个表达式并返回该表达式返回true的第一个元素:

实例:

a = [1,2,3,4,5,6,7,8,9,10].find{|el| el / 2 == 2 }
puts a.to_s

# ---- 输出结果 ----
4

3.8 detect

find的别名,用法和它相同。

3.9 reject

select的相反方法:为每个数组元素运行一个表达式,如果表达式为false,则在输出中包含该元素。

实例:

a = [1,2,3,4,5,6,7,8,9,10].reject{|e| e==2 || e==8 }
puts a.to_s

# ---- 输出结果 ----
[1, 3, 4, 5, 6, 7, 9, 10]

4. 次数迭代器

有的时候我们需要进行固定迭代次数的操作,这个时候我们通常有3种方式,使用范围(Range)、使用times方法,使用upto或者downto方法。

4.1 范围迭代

使用范围来固定迭代次数:

实例:

(1..5).each do |i|   
   puts i   
end   

# ---- 输出结果 ----
1
2
3
4
5

解释:因为 1..5 代表的是从 1~5 的所有数字,所以可以遍历 5 次。

在进行范围迭代的时候,我们可以使用step来选择每次迭代的步长。

实例:

(0..50).step(5) do |n|
  puts n
end

# ---- 输出结果 ----
0
5
10
15
20
25
30
35
40
45
50

解释:我们正常使用范围迭代的时候,都是每次递增1的,而使用step,我们可以选择每次迭代一次递增5个。

4.2 times 迭代

times我们在英文中翻译成次数,Ruby的Integer有一个很通俗易懂的实例方法名字就叫times,可以让我们循环固定次数。

实例:

5.times.each do |i|
  puts i
end

# ---- 输出结果 ----
0
1
2
3
4

解释:这里 5 调用了它的times方法,它可以简单理解成为创建了一个从 0~4 的数组。

实例:

5.times.to_a

# ---- 输出结果 ----
[0, 1, 2, 3, 4]

4.3 upto 和 downto 迭代

这两个同样也是 Integer 类的实例方法。

实例:

1.upto(5) do |n|   
  puts n   
end  

# ---- 输出结果 ----
1
2
3
4
5

解释:通俗的解释,从1一直迭代到5,和[1, 2, 3, 4 ,5]数组同理。

实例:

1.upto(5).to_a

# ---- 输出结果 ----
[1, 2, 3, 4, 5]

上述是一种递增迭代的形式,另外一种是递减的形式。

实例:

5.downto(1) do |n|
  puts n
end

# ---- 输出结果 ----
5
4
3
2
1

**解释:**会从5一直迭代到1,和 [5, 4, 3, 2, 1] 同理。

实例:

5.upto(1).to_a

# ---- 输出结果 ----
[5, 4, 3, 2, 1]

5. 小结

本小节中,我们了解了迭代类似于循环,学习了each迭代器,each_with_index迭代器,了解了各种迭代器的返回值,以及三种固定次数的迭代器:范围迭代、times迭代,uptodownto迭代。