13-ruby 迭代器详解
一、迭代器基本概念
1. 什么是迭代器
在Ruby中,迭代器是用于遍历集合(如数组、哈希、范围等)的特殊方法,它们允许你访问集合中的每个元素而不需要手动管理索引或计数器。
2. 迭代器特点
- 代码块支持:通常接受一个代码块(block)作为参数
- 内部迭代:由集合自身控制迭代过程
- 简洁安全:避免手动管理循环变量和边界条件
3. 与循环的区别
| 特性 | 迭代器 | 循环 |
|---|---|---|
| 控制方式 | 集合控制 | 程序员控制 |
| 安全性 | 更高 | 需要手动控制边界 |
| 代码量 | 通常更少 | 通常更多 |
| 适用场景 | 集合遍历 | 复杂循环逻辑 |
二、常用迭代器及示例
1. 基本迭代器
each - 最常用的迭代器
# `each` - 最常用的迭代器
# 数组遍历
[1, 2, 3].each { |num| puts num * 2 }
# 输出:
# 2
# 4
# 6
# 哈希遍历
{ a: 1, b: 2 }.each { |key, value| puts "#{key}: #{value}" }
# 输出:
# a: 1
# b: 2
each_with_index - 带索引的遍历
# each_with_index - 带索引的遍历
%w[a b c].each_with_index do |item, index|
puts "#{index}: #{item}"
end
# 输出:
# 0: a
# 1: b
# 2: c
2. 返回新集合的迭代器
map/collect - 转换集合
# map/collect - 转换集合
squares = [1, 2, 3].map { |n| n ** 2 }
p squares # [1, 4, 9]
upcased = %w[ruby python].map(&:upcase)
p upcased # ["RUBY", "PYTHON"]
select/find_all - 筛选元素
# select/find_all - 筛选元素
evens = (1..10).select { |n| n.even? }
p evens # [2, 4, 6, 8, 10]
reject - 排除元素
# reject - 排除元素
odds = (1..10).reject(&:even?)
p odds # [1, 3, 5, 7, 9]
3. 搜索类迭代器
find/detect - 查找单个元素
# `find`/`detect` - 查找单个元素
first_even = [1, 3, 5, 6, 7].find(&:even?)
p first_even # 6
any? - 检查任意元素满足条件
# any? - 检查任意元素满足条件
has_even = [1, 3, 5, 7].any?(&:even?)
p has_even # false
all? - 检查所有元素满足条件
# all? - 检查所有元素满足条件
all_positive = [1, 2, 3].all? { |n| n > 0 }
p all_positive # true
4. 聚合类迭代器
reduce/inject - 累积计算
# `reduce`/`inject` - 累积计算
sum = [1, 2, 3].reduce(0) { |total, n| total + n }
p sum # 6
product = [1, 2, 3, 4].inject(:*)
p product # 24
group_by - 分组
# group_by - 分组
grouped = %w[apple banana orange].group_by { |word| word.length }
p grouped
# {5=>["apple"], 6=>["banana", "orange"]}
5. 特殊迭代器
step - 步进迭代
# `step` - 步进迭代
(1..10).step(2) { |n| puts n }
# 输出:
# 1
# 3
# 5
# 7
# 9
cycle - 循环迭代
# cycle - 循环迭代
count = 0
[1, 2, 3].cycle(2) do |n|
puts n
count += 1
break if count >= 5
end
# 输出:
# 1
# 2
# 3
# 1
# 2
三、自定义迭代器
1. 基本自定义迭代器
# 基本自定义迭代器
class MyCollection
def initialize(data)
@data = data
end
def my_each
index = 0
while index < @data.length
yield @data[index]
index += 1
end
end
end
collection = MyCollection.new([10, 20, 30])
collection.my_each { |n| puts n * 2 }
# 输出:
# 20
# 40
# 60
2. 带枚举器的实现
# 带枚举器的实现
class MyRange
def initialize(start, stop)
@start = start
@stop = stop
end
def each
return enum_for(:each) unless block_given?
current = @start
while current <= @stop
yield current
current += 1
end
end
end
range = MyRange.new(1, 3)
range.each { |n| puts n }
# 输出:
# 1
# 2
# 3
# 可以转换为数组
p range.each.to_a # [1, 2, 3]
四、迭代器链式调用
# 迭代器链式调用
result = (1..100)
.select { |n| n % 3 == 0 } # 3的倍数
.reject { |n| n % 5 == 0 } # 排除5的倍数
.map { |n| n * 2 } # 每个元素乘以2
.take(5) # 取前5个
p result # [6, 12, 18, 24, 36]
五、性能考虑
惰性求值:使用lazy处理大数据集
# **惰性求值**:使用`lazy`处理大数据集
large_data = (1..Float::INFINITY).lazy
.select(&:even?)
.map { |x| x * x }
.take(5)
.to_a
p large_data # [4, 16, 36, 64, 100]
方法选择:
- 需要结果集合:
map,select - 只需要副作用:
each - 大数据处理:考虑
lazy
符号到Proc:使用&:method简化代码
# 以下两种方式等效
words.map { |w| w.upcase }
words.map(&:upcase)
六、最佳实践
- 优先使用迭代器而非手动循环
- 链式调用时注意可读性
- 大数据集考虑使用
lazy - 明确区分有副作用和无副作用的迭代器
- 自定义迭代器时实现
to_enum/enum_for支持
41

被折叠的 条评论
为什么被折叠?



