1. 什么是迭代器?
迭代器是一种方法,通过调用它可以逐个处理集合中的元素。Ruby 的迭代器通常接受块(do ... end
或 { ... }
),在块中定义对每个元素的操作。它们替代了传统语言中的 for
循环,使代码更优雅。
核心理念
- 块驱动:迭代器通过
yield
将控制权交给块。 - 集合操作:主要用于数组、哈希、范围等。
2. 常见迭代器
each
遍历集合中的每个元素。
- 数组
1 2 3 4 | fruits = ["苹果", "香蕉", "橙子"] fruits.each do |fruit| puts "我喜欢 #{fruit}" end |
输出:
1 2 3 | 我喜欢 苹果 我喜欢 香蕉 我喜欢 橙子 |
- 哈希
1 2 3 4 | person = { 姓名: "张三", 年龄: 25 } person.each do |key, value| puts "#{key}: #{value}" end |
输出:
1 2 | 姓名: 张三 年龄: 25 |
- 范围
1 | (1..3).each { |n| puts "第 #{n} 次" } |
输出:
1 2 3 | 第 1 次 第 2 次 第 3 次 |
each_with_index
遍历时带上索引。
1 2 3 4 | tasks = ["写代码", "开会", "休息"] tasks.each_with_index do |task, index| puts "#{index + 1}. #{task}" end |
输出:
1 2 3 | 1. 写代码 2. 开会 3. 休息 |
map
/ collect
对每个元素应用操作,返回新数组。
1 2 3 4 5 | numbers = [1, 2, 3] doubled = numbers.map do |n| n * 2 end puts doubled # [2, 4, 6] |
单行写法:
1 2 | squared = [1, 2, 3].map { |n| n ** 2 } puts squared # [1, 4, 9] |
select
/ find_all
筛选符合条件的元素,返回新数组。
1 2 3 | numbers = [1, 2, 3, 4, 5] even = numbers.select { |n| n % 2 == 0 } puts even # [2, 4] |
reject
排除符合条件的元素,返回新数组。
1 2 | odds = numbers.reject { |n| n % 2 == 0 } puts odds # [1, 3, 5] |
find
/ detect
返回第一个符合条件的元素。
1 2 | first_even = numbers.find { |n| n % 2 == 0 } puts first_even # 2 |
reduce
/ inject
累积操作,返回单一结果。
1 2 3 4 5 6 | sum = [1, 2, 3, 4].reduce(0) { |total, n| total + n } puts sum # 10 # 指定初始值可选 product = [1, 2, 3].reduce(:*) # 使用符号简化 puts product # 6 |
times
重复执行指定次数。
1 2 3 | 5.times do |i| puts "第 #{i + 1} 次问候:你好!" end |
输出:
1 2 3 4 | 第 1 次问候:你好! 第 2 次问候:你好! ... 第 5 次问候:你好! |
3. 自定义迭代器
通过 yield
创建自定义迭代器。
示例
1 2 3 4 5 6 7 | def repeat(n) n.times { |i| yield(i) } end repeat(3) do |count| puts "计数:#{count}" end |
输出:
1 2 3 | 计数:0 计数:1 计数:2 |
带条件的迭代器
1 2 3 4 5 6 7 | def filter_numbers(numbers) numbers.each do |n| yield(n) if n > 0 end end filter_numbers([-1, 2, 0, 3]) { |n| puts "正数:#{n}" } |
输出:
1 2 | 正数:2 正数:3 |
4. 迭代器的返回值
each
:返回原集合。map
:返回新数组。select
:返回筛选后的数组。reduce
:返回累积结果。
示例
1 2 3 4 5 6 | numbers = [1, 2, 3] result = numbers.each { |n| puts n } puts result # [1, 2, 3](返回原数组) mapped = numbers.map { |n| n * 2 } puts mapped # [2, 4, 6] |
5. 中文支持示例
遍历任务
1 2 3 4 | 任务 = ["写代码", "开会", "休息"] 任务.each_with_index do |task, i| puts "任务 #{i + 1}:#{task}" end |
输出:
1 2 3 | 任务 1:写代码 任务 2:开会 任务 3:休息 |
转换中文数据
1 2 3 | names = ["张三", "李四", "王五"] greetings = names.map { |name| "你好,#{name}!" } puts greetings |
输出:
1 2 3 | 你好,张三! 你好,李四! 你好,王五! |
6. 实践案例
案例 1:成绩统计
1 2 3 4 | scores = [85, 92, 78, 95] average = scores.reduce(0, :+) / scores.length.to_f highest = scores.max puts "平均分:#{average},最高分:#{highest}" |
输出:
1 | 平均分:87.5,最高分:95 |
案例 2:任务过滤
1 2 3 | tasks = ["写代码", "开会", "休息", "测试"] long_tasks = tasks.select { |task| task.length > 2 } puts "长任务:#{long_tasks.join(', ')}" |
输出:
1 | 长任务:写代码, 休息 |
案例 3:自定义计数器
1 2 3 4 5 6 7 | def count_to(limit) (1..limit).each do |n| yield(n) end end count_to(3) { |n| puts "计数:#{n}" } |
输出:
1 2 3 | 计数:1 计数:2 计数:3 |
7. 迭代器的控制流
break
跳出迭代:
1 2 3 4 | [1, 2, 3, 4].each do |n| puts n break if n == 2 end |
输出:
1 2 | 1 2 |
next
跳到下一次迭代:
1 2 3 4 | [1, 2, 3].each do |n| next if n % 2 == 0 puts "奇数:#{n}" end |
输出:
1 2 | 奇数:1 奇数:3 |
redo
重复当前迭代:
1 2 3 4 5 6 | count = 0 [1, 2].each do |n| count += 1 puts "当前:#{n}" redo if count == 1 end |
输出:
1 2 3 | 当前:1 当前:1 当前:2 |
8. 注意事项
- 性能:迭代器是惰性计算的,适合大数据处理。
- 返回值:注意迭代器的返回值是否符合需求(例如
each
vsmap
)。 - 块参数:参数数量需与迭代器传递的值匹配。
9. 高级用法
链式调用
1 2 3 4 5 | numbers = [1, 2, 3, 4, 5] result = numbers .select { |n| n % 2 == 0 } .map { |n| n * 10 } puts result # [20, 40] |
自定义对象迭代
1 2 3 4 5 6 7 8 9 10 11 12 | class TaskList def initialize(tasks) @tasks = tasks end def each @tasks.each { |task| yield(task) } end end list = TaskList.new(["写代码", "休息"]) list.each { |task| puts "任务:#{task}" } |
输出:
1 2 | 任务:写代码 任务:休息 |
下一步
- 练习:告诉我你想用迭代器做什么(遍历、转换等),我可以设计一个例子。
- 问题解答:对迭代器用法有疑问吗?直接问我!
- 深入学习:想了解迭代器与 Enumerator 或 Lazy 迭代吗?我可以继续讲解。
你现在想做什么?写代码、问问题,还是其他?
发表回复