Ruby 迭代器

在 Ruby 中,迭代器(Iterator) 是一种用于遍历集合(如数组、哈希、范围等)的机制,通常以方法的形式结合 块(block) 使用。Ruby 的迭代器通过 Enumerable 模块和特定集合类(如 Array, Hash, Range)提供,极大简化了循环操作,使代码更简洁和优雅。以下是对 Ruby 迭代器的中文讲解,涵盖核心概念、常见迭代器及用法,力求简洁清晰。


1. 什么是迭代器?

迭代器是 Ruby 中用于遍历数据集合的方法,通常接受一个块(block)来定义每次迭代的行为。Ruby 的迭代器依赖于 yield 或显式块参数(&block)来执行用户提供的代码。迭代器是 Ruby 循环的首选方式,优于传统的 forwhile 循环,因为它们更简洁且符合 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 的迭代器是处理集合的核心工具,通过 eachEnumerable 模块(如 map, select, reduce)提供强大而简洁的遍历和转换功能。迭代器结合块使用,优于传统循环,符合 Ruby 的简洁和优雅哲学。自定义迭代器进一步增强了灵活性。注意块作用域和返回值差异,可优化代码效率和可读性。

如果你有具体问题或需要更详细的示例,请告诉我!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注