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