13-ruby 迭代器详解

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)

六、最佳实践

  1. 优先使用迭代器而非手动循环
  2. 链式调用时注意可读性
  3. 大数据集考虑使用lazy
  4. 明确区分有副作用和无副作用的迭代器
  5. 自定义迭代器时实现to_enum/enum_for支持
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海洋的渔夫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值