Ruby 迭代器
在 Ruby 中,迭代器(Iterator) 是一种用于遍历集合(如数组、哈希、范围等)的机制,通常以方法的形式结合 块(block) 使用。Ruby 的迭代器通过 Enumerable
模块和特定集合类(如 Array
, Hash
, Range
)提供,极大简化了循环操作,使代码更简洁和优雅。以下是对 Ruby 迭代器的中文讲解,涵盖核心概念、常见迭代器及用法,力求简洁清晰。
1. 什么是迭代器?
迭代器是 Ruby 中用于遍历数据集合的方法,通常接受一个块(block)来定义每次迭代的行为。Ruby 的迭代器依赖于 yield
或显式块参数(&block
)来执行用户提供的代码。迭代器是 Ruby 循环的首选方式,优于传统的 for
或 while
循环,因为它们更简洁且符合 Ruby 的哲学。
2. 核心迭代器:each
each
是最基本的迭代器,用于遍历集合的每个元素。
# 数组
[1, 2, 3].each { |n| puts n * 2 }
# 输出:
# 2
# 4
# 6
# 哈希
hash = { name: "Alice", age: 25 }
hash.each { |key, value| puts "#{key}: #{value}" }
# 输出:
# name: Alice
# age: 25
说明:
each
方法遍历集合,将每个元素(或键值对)传递给块。- 块使用
do...end
(多行)或{}
(单行)定义。
3. Enumerable 模块与迭代器
Ruby 的 Enumerable
模块为支持 each
方法的类(如 Array
, Hash
, Range
)提供了丰富的迭代器。使用 Enumerable
迭代器需确保类定义了 each
方法。
常用 Enumerable 迭代器
- map / collect:遍历并转换元素,返回新数组。
arr = [1, 2, 3]
result = arr.map { |n| n * 2 }
puts result # 输出:[2, 4, 6]
- select / find_all:筛选符合条件的元素,返回新数组。
evens = arr.select { |n| n.even? }
puts evens # 输出:[2]
- reject:筛选不符合条件的元素,返回新数组。
odds = arr.reject { |n| n.even? }
puts odds # 输出:[1, 3]
- find / detect:返回第一个符合条件的元素,或
nil
。
result = arr.find { |n| n > 2 }
puts result # 输出:3
- reduce / inject:累积操作,返回单一结果。
sum = arr.reduce(0) { |acc, n| acc + n }
puts sum # 输出:6(0 + 1 + 2 + 3)
- any?:检查是否存在满足条件的元素。
puts arr.any? { |n| n > 2 } # 输出:true
- all?:检查是否所有元素满足条件。
puts arr.all? { |n| n > 0 } # 输出:true
- none?:检查是否没有元素满足条件。
puts arr.none? { |n| n < 0 } # 输出:true
说明:
Enumerable
方法依赖块来定义逻辑,灵活性高。- 方法如
map
,select
返回新集合,不会修改原对象;带!
的版本(如map!
)会修改原对象。
4. 其他常见迭代器
除了 Enumerable
提供的迭代器,Ruby 集合类还有特定迭代器:
- Array 特定迭代器:
each_index
:遍历索引。ruby arr = ['a', 'b', 'c'] arr.each_index { |i| puts i } # 输出:0, 1, 2
reverse_each
:反向遍历。ruby arr.reverse_each { |x| puts x } # 输出:c, b, a
- Hash 特定迭代器:
each_key
:遍历键。ruby hash = { a: 1, b: 2 } hash.each_key { |k| puts k } # 输出:a, b
each_value
:遍历值。ruby hash.each_value { |v| puts v } # 输出:1, 2
- Range 特定迭代器:
step
:按指定步长遍历。ruby (1..10).step(2) { |n| puts n } # 输出:1, 3, 5, 7, 9
5. 自定义迭代器
可以通过 yield
或 &block
定义自定义迭代器。
def my_each(array)
i = 0
while i < array.length
yield(array[i])
i += 1
end
array
end
my_each([1, 2, 3]) { |n| puts n * 2 }
# 输出:
# 2
# 4
# 6
显式块参数:
def call_block(&block)
block.call(42)
end
call_block { |n| puts "Number: #{n}" } # 输出:Number: 42
6. 迭代器与循环的对比
- 传统循环(如
for
,while
): - 显式控制索引或条件。
- 代码较冗长,易出错。
arr = [1, 2, 3]
for i in 0...arr.length
puts arr[i]
end
- 迭代器:
- 抽象化遍历逻辑,代码更简洁。
- 通过块定制行为,灵活性高。
arr.each { |n| puts n }
推荐:优先使用迭代器,符合 Ruby 的简洁哲学。
7. 注意事项
- 块的作用域:块可以访问外部变量(闭包特性)。
sum = 0
[1, 2, 3].each { |n| sum += n }
puts sum # 输出:6
- 性能:迭代器通常比传统循环更高效,尤其结合
Enumerable
方法。 - 返回值:
each
返回原集合。map
返回新数组。select
/reject
返回筛选后的新集合。reduce
返回累积结果。- 空集合:迭代空集合时,块不会执行。
[].each { |n| puts n } # 无输出
- 链式调用:迭代器可链式使用,增强可读性。
arr = [1, 2, 3, 4]
result = arr.select { |n| n.even? }.map { |n| n * 2 }
puts result # 输出:[4, 8]
8. 示例:综合应用
module MyIterators
def multiply_by(factor)
map { |n| n * factor }
end
end
class Array
include MyIterators
end
arr = [1, 2, 3]
puts arr.multiply_by(3) # 输出:[3, 6, 9]
# 哈希示例
hash = { a: 1, b: 2, c: 3 }
puts hash.select { |k, v| v > 1 }.keys # 输出:[:b, :c]
9. 总结
Ruby 的迭代器是处理集合的核心工具,通过 each
和 Enumerable
模块(如 map
, select
, reduce
)提供强大而简洁的遍历和转换功能。迭代器结合块使用,优于传统循环,符合 Ruby 的简洁和优雅哲学。自定义迭代器进一步增强了灵活性。注意块作用域和返回值差异,可优化代码效率和可读性。
如果你有具体问题或需要更详细的示例,请告诉我!