Ruby 迭代器
迭代器是Ruby中一个强大而优雅的特性,它提供了一种遍历集合元素的标准化方式。Ruby的迭代器不仅包括内置的迭代方法,还支持自定义迭代器,使得代码更加简洁和表达力强。本章将详细介绍Ruby中迭代器的使用方法和最佳实践。
🎯 迭代器基础
什么是迭代器
迭代器是一种设计模式,它提供了一种访问集合元素的方法,而无需暴露集合的内部表示。在Ruby中,迭代器通常以块的形式实现,使得代码更加简洁和易读。
ruby
# 基本迭代器使用
[1, 2, 3, 4, 5].each { |n| puts n }
# 输出:
# 1
# 2
# 3
# 4
# 5
# 使用do...end语法
[1, 2, 3, 4, 5].each do |n|
puts n
end
# 迭代器与块参数
fruits = ["苹果", "香蕉", "橙子"]
fruits.each_with_index do |fruit, index|
puts "#{index + 1}. #{fruit}"
end
# 输出:
# 1. 苹果
# 2. 香蕉
# 3. 橙子内置迭代器方法
ruby
# each - 基本迭代
[1, 2, 3].each { |n| puts n }
# map/collect - 转换每个元素
squared = [1, 2, 3, 4].map { |n| n ** 2 }
puts squared.inspect # [1, 4, 9, 16]
# select/find_all - 筛选满足条件的元素
evens = [1, 2, 3, 4, 5, 6].select(&:even?)
puts evens.inspect # [2, 4, 6]
# reject - 排除满足条件的元素
odds = [1, 2, 3, 4, 5, 6].reject(&:even?)
puts odds.inspect # [1, 3, 5]
# find/detect - 查找第一个满足条件的元素
first_even = [1, 3, 4, 5, 6].find(&:even?)
puts first_even # 4
# find_all - 查找所有满足条件的元素(与select相同)
all_evens = [1, 2, 3, 4, 5, 6].find_all(&:even?)
puts all_evens.inspect # [2, 4, 6]🔁 常用迭代器方法
基本迭代方法
ruby
# each - 对每个元素执行操作
numbers = [1, 2, 3, 4, 5]
numbers.each { |n| puts "数字: #{n}" }
# each_with_index - 带索引的迭代
fruits = ["苹果", "香蕉", "橙子"]
fruits.each_with_index { |fruit, index| puts "#{index}: #{fruit}" }
# reverse_each - 反向迭代
[1, 2, 3].reverse_each { |n| puts n }
# 输出: 3, 2, 1
# each_slice - 分块迭代
(1..10).each_slice(3) { |slice| puts slice.inspect }
# [1, 2, 3]
# [4, 5, 6]
# [7, 8, 9]
# [10]
# each_cons - 连续元素迭代
(1..5).each_cons(3) { |cons| puts cons.inspect }
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]转换迭代器
ruby
# map/collect - 转换每个元素
numbers = [1, 2, 3, 4, 5]
squared = numbers.map { |n| n ** 2 }
puts squared.inspect # [1, 4, 9, 16, 25]
# map! - 就地转换
numbers.map! { |n| n * 2 }
puts numbers.inspect # [2, 4, 6, 8, 10]
# flat_map - 扁平化映射
nested = [[1, 2], [3, 4], [5, 6]]
flattened = nested.flat_map { |arr| arr }
puts flattened.inspect # [1, 2, 3, 4, 5, 6]
# collect_concat - 与flat_map相同
result = nested.collect_concat { |arr| arr.map { |n| n * 2 } }
puts result.inspect # [2, 4, 6, 8, 10, 12]
# zip - 合并多个数组
letters = ["a", "b", "c"]
numbers = [1, 2, 3]
combined = letters.zip(numbers)
puts combined.inspect # [["a", 1], ["b", 2], ["c", 3]]
letters.zip(numbers) { |letter, number| puts "#{letter}: #{number}" }
# a: 1
# b: 2
# c: 3筛选迭代器
ruby
# select/find_all - 选择满足条件的元素
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = numbers.select { |n| n.even? }
puts evens.inspect # [2, 4, 6, 8, 10]
# reject - 排除满足条件的元素
odds = numbers.reject { |n| n.even? }
puts odds.inspect # [1, 3, 5, 7, 9]
# grep - 使用正则表达式筛选
words = ["apple", "banana", "cherry", "date"]
a_words = words.grep(/^a/)
puts a_words.inspect # ["apple"]
# grep_v - 排除匹配正则表达式的元素
non_a_words = words.grep_v(/^a/)
puts non_a_words.inspect # ["banana", "cherry", "date"]
# take_while - 从开始取满足条件的元素
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
small_numbers = numbers.take_while { |n| n < 5 }
puts small_numbers.inspect # [1, 2, 3, 4]
# drop_while - 跳过开始满足条件的元素
large_numbers = numbers.drop_while { |n| n < 5 }
puts large_numbers.inspect # [5, 6, 7, 8, 9, 10]查找迭代器
ruby
# find/detect - 查找第一个满足条件的元素
numbers = [1, 3, 5, 6, 7, 8]
first_even = numbers.find(&:even?)
puts first_even # 6
# find_index - 查找第一个满足条件的元素索引
index = numbers.find_index(&:even?)
puts index # 3
# any? - 检查是否有元素满足条件
has_even = numbers.any?(&:even?)
puts has_even # true
# all? - 检查是否所有元素都满足条件
all_even = numbers.all?(&:even?)
puts all_even # false
# none? - 检查是否没有元素满足条件
no_negative = numbers.none? { |n| n < 0 }
puts no_negative # true
# one? - 检查是否只有一个元素满足条件
one_even = numbers.one?(&:even?)
puts one_even # false
# include?/member? - 检查是否包含特定元素
has_five = numbers.include?(5)
puts has_five # true🔢 数值迭代器
数值范围迭代
ruby
# times - 从0开始迭代指定次数
5.times { |i| puts "第#{i + 1}次" }
# 第1次
# 第2次
# 第3次
# 第4次
# 第5次
# upto - 从当前数向上迭代到指定数
1.upto(5) { |n| puts n }
# downto - 从当前数向下迭代到指定数
5.downto(1) { |n| puts n }
# step - 按指定步长迭代
1.step(10, 2) { |n| puts n } # 1, 3, 5, 7, 9
# step with block parameters
1.step(10, 2) { |n| puts "数字: #{n}" }
# 使用范围迭代
(1..5).each { |n| puts n }
('a'..'e').each { |char| puts char }数值累积迭代
ruby
# reduce/inject - 累积操作
numbers = [1, 2, 3, 4, 5]
# 求和
sum = numbers.reduce(0) { |total, n| total + n }
puts sum # 15
# 使用符号简写
sum = numbers.reduce(0, :+)
puts sum # 15
# 求积
product = numbers.reduce(1) { |total, n| total * n }
puts product # 120
# 找最大值
max = numbers.reduce { |max, n| n > max ? n : max }
puts max # 5
# 字符串连接
words = ["Hello", "World", "Ruby"]
sentence = words.reduce("") { |result, word| result + " " + word }.strip
puts sentence # Hello World Ruby
# 构建哈希
pairs = [["name", "张三"], ["age", "25"], ["city", "北京"]]
hash = pairs.reduce({}) do |result, pair|
result[pair[0]] = pair[1]
result
end
puts hash.inspect # {"name"=>"张三", "age"=>"25", "city"=>"北京"}🔄 高级迭代器
分组和分区
ruby
# group_by - 按条件分组
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
grouped = numbers.group_by { |n| n.even? ? "偶数" : "奇数" }
puts grouped
# {"奇数"=>[1, 3, 5, 7, 9], "偶数"=>[2, 4, 6, 8, 10]}
# partition - 分区为两组
partitioned = numbers.partition(&:even?)
puts partitioned.inspect
# [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]
# chunk - 按连续相同条件分块
data = [1, 2, 4, 6, 7, 9, 10, 12]
chunks = data.chunk { |n| n.even? }.to_a
puts chunks.inspect
# [[true, [1]], [false, [2, 4, 6]], [true, [7]], [false, [9, 10, 12]]]
# slice_before - 在满足条件前切片
words = ["apple", "ant", "banana", "bee", "cherry", "cat"]
sliced = words.slice_before { |word| word.start_with?('a') }.to_a
puts sliced.inspect
# [["apple"], ["ant", "banana"], ["bee", "cherry"], ["cat"]]
# slice_after - 在满足条件后切片
sliced_after = words.slice_after { |word| word.end_with?('e') }.to_a
puts sliced_after.inspect
# [["apple", "ant"], ["banana", "bee", "cherry"], ["cat"]]排序迭代器
ruby
# sort - 排序
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted = numbers.sort
puts sorted.inspect # [1, 1, 2, 3, 4, 5, 6, 9]
# sort_by - 按特定条件排序
words = ["apple", "banana", "cherry", "date"]
by_length = words.sort_by(&:length)
puts by_length.inspect # ["date", "apple", "cherry", "banana"]
# reverse - 反转
reversed = numbers.reverse
puts reversed.inspect # [6, 2, 9, 5, 1, 4, 1, 3]
# shuffle - 随机排列
shuffled = numbers.shuffle
puts shuffled.inspect # 随机顺序
# sample - 随机采样
sampled = numbers.sample(3)
puts sampled.inspect # 随机3个元素🎯 自定义迭代器
创建自定义迭代器
ruby
class CustomCollection
def initialize(items)
@items = items
end
# 基本迭代器
def each
@items.each { |item| yield item }
end
# 带索引的迭代器
def each_with_index
@items.each_with_index { |item, index| yield item, index }
end
# 自定义映射迭代器
def custom_map
result = []
each { |item| result << yield(item) }
result
end
# 条件迭代器
def each_if(&condition)
each do |item|
yield item if condition.call(item)
end
end
# 反向迭代器
def reverse_each
(@items.length - 1).downto(0) do |i|
yield @items[i]
end
end
end
# 使用自定义迭代器
collection = CustomCollection.new([1, 2, 3, 4, 5])
# 基本迭代
collection.each { |n| puts n }
# 带索引迭代
collection.each_with_index { |item, index| puts "#{index}: #{item}" }
# 自定义映射
squared = collection.custom_map { |n| n ** 2 }
puts squared.inspect # [1, 4, 9, 16, 25]
# 条件迭代
collection.each_if { |n| n.even? } { |n| puts "偶数: #{n}" }
# 反向迭代
collection.reverse_each { |n| puts "反向: #{n}" }Enumerable模块
ruby
class NumberSequence
include Enumerable
def initialize(start, finish)
@start = start
@finish = finish
end
# 实现each方法以获得Enumerable的所有功能
def each
(@start..@finish).each { |n| yield n }
end
# 可以覆盖特定方法以提高性能
def size
@finish - @start + 1
end
end
# 使用包含Enumerable的类
sequence = NumberSequence.new(1, 10)
# 现在可以使用所有Enumerable方法
puts sequence.map { |n| n * 2 }.inspect # [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
puts sequence.select(&:even?).inspect # [2, 4, 6, 8, 10]
puts sequence.find { |n| n > 5 } # 6
puts sequence.all? { |n| n > 0 } # true
puts sequence.any? { |n| n > 15 } # false
puts sequence.count # 10🎯 迭代器实践示例
数据处理管道
ruby
class DataPipeline
def initialize(data)
@data = data
end
# 链式方法调用
def filter(&block)
DataPipeline.new(@data.select(&block))
end
def map(&block)
DataPipeline.new(@data.map(&block))
end
def sort(&block)
DataPipeline.new(@data.sort(&block))
end
def take(n)
DataPipeline.new(@data.take(n))
end
def group_by(&block)
@data.group_by(&block)
end
def result
@data
end
end
# 使用数据处理管道
numbers = (1..20).to_a
result = DataPipeline.new(numbers)
.filter { |n| n.even? }
.map { |n| n ** 2 }
.sort { |a, b| b <=> a }
.take(5)
.result
puts result.inspect # [400, 256, 144, 64, 16]文件行处理
ruby
class FileProcessor
def self.process_lines(filename, &block)
File.open(filename, 'r') do |file|
file.each_line.with_index(1) do |line, line_number|
yield line.chomp, line_number
end
end
rescue Errno::ENOENT
puts "文件 #{filename} 不存在"
rescue => e
puts "处理文件时出错: #{e.message}"
end
def self.filter_lines(filename, pattern)
lines = []
process_lines(filename) do |line, line_number|
lines << { line: line, number: line_number } if line.match?(pattern)
end
lines
end
def self.transform_lines(filename, &transformer)
transformed = []
process_lines(filename) do |line, line_number|
transformed << { original: line, transformed: transformer.call(line), number: line_number }
end
transformed
end
end
# 使用文件处理器(假设存在test.txt文件)
# filtered = FileProcessor.filter_lines('test.txt', /ruby/i)
# transformed = FileProcessor.transform_lines('test.txt') { |line| line.upcase }迭代器组合器
ruby
class IteratorCombinator
# 组合多个迭代器操作
def self.process(data, operations)
result = data
operations.each do |operation|
case operation[:type]
when :map
result = result.map(&operation[:block])
when :select
result = result.select(&operation[:block])
when :reject
result = result.reject(&operation[:block])
when :sort
result = result.sort(&operation[:block])
when :take
result = result.take(operation[:count])
end
end
result
end
# 创建操作链
def self.operation_chain
[]
end
# 添加映射操作
def self.add_map(chain, &block)
chain << { type: :map, block: block }
end
# 添加筛选操作
def self.add_select(chain, &block)
chain << { type: :select, block: block }
end
# 添加排序操作
def self.add_sort(chain, &block)
chain << { type: :sort, block: block }
end
end
# 使用迭代器组合器
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
operations = IteratorCombinator.operation_chain
IteratorCombinator.add_select(operations) { |n| n.even? }
IteratorCombinator.add_map(operations) { |n| n ** 2 }
IteratorCombinator.add_sort(operations) { |a, b| b <=> a }
result = IteratorCombinator.process(numbers, operations)
puts result.inspect # [100, 64, 36, 16, 4]📊 性能优化
Lazy迭代器
ruby
# 对于大数据集,使用lazy迭代器
large_range = 1..1_000_000
# 普通方式(会处理所有元素)
# result = large_range.select { |n| n.even? }.map { |n| n ** 2 }.first(10)
# 使用lazy延迟计算
result = large_range.lazy
.select { |n| n.even? }
.map { |n| n ** 2 }
.first(10)
puts result.inspect # [4, 16, 36, 64, 100, 144, 196, 256, 324, 400]
# 自定义lazy迭代器
class CustomLazyProcessor
def initialize(data)
@data = data
end
def lazy
LazyWrapper.new(@data)
end
end
class LazyWrapper
def initialize(data)
@data = data
end
def map(&block)
LazyMap.new(self, block)
end
def select(&block)
LazySelect.new(self, block)
end
def take(n)
LazyTake.new(self, n)
end
def to_a
@data.to_a
end
end
class LazyMap < LazyWrapper
def initialize(parent, block)
@parent = parent
@block = block
end
def to_a
@parent.to_a.map(&@block)
end
end
class LazySelect < LazyWrapper
def initialize(parent, block)
@parent = parent
@block = block
end
def to_a
@parent.to_a.select(&@block)
end
end
class LazyTake < LazyWrapper
def initialize(parent, n)
@parent = parent
@n = n
end
def to_a
@parent.to_a.take(@n)
end
end迭代器性能优化
ruby
# 避免不必要的数组创建
# 低效方式
def inefficient_process(data)
data.map { |x| x * 2 }
.select { |x| x > 10 }
.map { |x| x.to_s }
end
# 高效方式
def efficient_process(data)
result = []
data.each do |x|
doubled = x * 2
if doubled > 10
result << doubled.to_s
end
end
result
end
# 使用each_with_object累积结果
def process_with_object(data)
data.each_with_object([]) do |x, result|
doubled = x * 2
result << doubled.to_s if doubled > 10
end
end
# 批量处理大数据集
def batch_process(data, batch_size = 1000)
data.each_slice(batch_size) do |batch|
# 处理每个批次
processed_batch = batch.map { |x| x * 2 }
# 执行其他操作...
end
end🎯 迭代器最佳实践
1. 选择合适的迭代器方法
ruby
# 对于简单遍历,使用each
[1, 2, 3].each { |n| puts n }
# 对于转换,使用map
squared = [1, 2, 3].map { |n| n ** 2 }
# 对于筛选,使用select
evens = [1, 2, 3, 4, 5].select(&:even?)
# 对于检查条件,使用any?/all?
has_even = [1, 2, 3].any?(&:even?) # true
all_positive = [1, 2, 3].all? { |n| n > 0 } # true
# 对于累积操作,使用reduce
sum = [1, 2, 3, 4, 5].reduce(:+)
# 对于查找,使用find
first_even = [1, 3, 4, 5].find(&:even?) # 42. 块参数的最佳实践
ruby
# 使用有意义的参数名
users.each { |user| puts user.name }
# 对于简单的单参数块,使用符号简写
names = users.map(&:name)
evens = numbers.select(&:even?)
# 对于多参数块,明确命名
users.each_with_index { |user, index| puts "#{index}: #{user.name}" }
# 避免在块中修改外部变量
# 不推荐
total = 0
numbers.each { |n| total += n }
# 推荐
total = numbers.reduce(0, :+)
# 或者
total = numbers.sum3. 错误处理和边界情况
ruby
class SafeIterator
# 安全的迭代处理
def self.safe_each(collection, &block)
return [] unless collection.respond_to?(:each)
result = []
collection.each do |item|
begin
result << yield(item) if block_given?
rescue => e
puts "处理元素时出错: #{e.message}"
result << nil
end
end
result
end
# 安全的映射
def self.safe_map(collection, &block)
return [] unless collection.respond_to?(:map) && block_given?
collection.map do |item|
begin
yield(item)
rescue => e
puts "转换元素时出错: #{e.message}"
item # 返回原值
end
end
end
# 安全的筛选
def self.safe_select(collection, &block)
return [] unless collection.respond_to?(:select) && block_given?
collection.select do |item|
begin
yield(item)
rescue => e
puts "筛选元素时出错: #{e.message}"
false # 默认不包含出错元素
end
end
end
end
# 使用安全迭代器
data = [1, 2, "invalid", 4, 5]
squared = SafeIterator.safe_map(data) { |n| n ** 2 }
puts squared.inspect # [1, 4, "invalid", 16, 25] (处理错误时返回原值)4. 迭代器在实际应用中的使用
ruby
# 用户数据处理
class UserProcessor
def initialize(users)
@users = users
end
# 活跃用户
def active_users
@users.select(&:active?)
end
# 按年龄分组
def group_by_age_group
@users.group_by do |user|
case user.age
when 0..17 then "未成年"
when 18..35 then "青年"
when 36..60 then "中年"
else "老年"
end
end
end
# 计算平均年龄
def average_age
active_users.map(&:age).reduce(0.0, :+) / active_users.size
end
# 查找VIP用户
def vip_users
@users.select { |user| user.level >= 5 }
end
# 用户名列表
def usernames
@users.map(&:username).sort
end
end
# 订单处理
class OrderProcessor
def initialize(orders)
@orders = orders
end
# 按状态分组订单
def group_by_status
@orders.group_by(&:status)
end
# 计算总收入
def total_revenue
@orders.reduce(0) { |sum, order| sum + order.amount }
end
# 高价值订单
def high_value_orders(threshold = 1000)
@orders.select { |order| order.amount > threshold }
end
# 按月份统计订单
def orders_by_month
@orders.group_by { |order| order.created_at.strftime("%Y-%m") }
end
# 最近的订单
def recent_orders(days = 30)
cutoff_date = Date.today - days
@orders.select { |order| order.created_at >= cutoff_date }
end
end📚 下一步学习
掌握了Ruby迭代器后,建议继续学习:
- Ruby 文件处理及I/O - 了解文件读写操作
- Ruby 异常处理 - 掌握错误处理机制
- Ruby 面向对象 - 深入理解面向对象编程
- Ruby 块及模块(Module) - 了解代码块和模块化编程
继续您的Ruby学习之旅吧!