Skip to content

Ruby 迭代器

Ruby 迭代器(Iterators):each、map 等

Section titled “Ruby 迭代器(Iterators):each、map 等”

迭代器(Iterators)是反复调用代码块来处理集合(如 Array 或 Hash)中每个元素的方法。它们是 Ruby 处理集合时表达力强且简洁风格的基石。

Ruby 的 Enumerable 模块混入(mixed into)了 Array 和 Hash 等类中,提供了丰富的迭代器方法。我们将重点介绍 each 和 map(也称为 collect),并简要提及其他一些方法。

each 迭代器是最基础的迭代器之一。它对集合中的每个元素执行给定的块一次,并将元素作为参数传递给块。each 总是返回调用它的原始集合。

collection.each do |variable|
# code to execute for each element
end
# Or for single-line blocks:
collection.each { |variable| # code }
#!/usr/bin/env ruby
numbers = [1, 2, 3, 4, 5]
puts "Iterating through numbers:"
numbers.each do |number|
puts "Current number: #{number}"
end
# The return value of each is the original array
returned_value = numbers.each { |n| n * 2 } # The block's result is discarded by each
puts "Returned value from 'each': #{returned_value.inspect}" #=> [1, 2, 3, 4, 5]

这将产生以下结果:

Iterating through numbers:
Current number: 1
Current number: 2
Current number: 3
Current number: 4
Current number: 5
Returned value from 'each': [1, 2, 3, 4, 5]
#!/usr/bin/env ruby
capitals = { "USA" => "Washington D.C.", "France" => "Paris", "Japan" => "Tokyo" }
capitals.each do |country, capital|
puts "The capital of #{country} is #{capital}."
end
# For hashes, each_pair is an alias for each.
# You can also iterate over just keys or values:
capitals.each_key { |country| puts "Country: #{country}" }
capitals.each_value { |capital_city| puts "Capital: #{capital_city}" }

这将产生:

The capital of USA is Washington D.C.
The capital of France is Paris.
The capital of Japan is Tokyo.
Country: USA
Country: France
Country: Japan
Capital: Washington D.C.
Capital: Paris
Capital: Tokyo

map 迭代器(别名为 collect)用于转换集合。它迭代集合中的每个元素,执行块,并构建一个新数组,其中包含块对每个元素的返回值。

new_array = collection.map do |variable|
# code whose return value will be part of the new array
end
# Or for single-line blocks:
new_array = collection.map { |variable| # expression }
#!/usr/bin/env ruby
original_numbers = [1, 2, 3, 4, 5]
# Create a new array with each number squared
squared_numbers = original_numbers.map do |n|
n * n
end
puts "Original numbers: #{original_numbers.inspect}"
puts "Squared numbers: #{squared_numbers.inspect}"
# Using the alias 'collect'
uppercased_fruits = ["apple", "banana", "cherry"].collect { |fruit| fruit.upcase }
puts "Uppercased fruits: #{uppercased_fruits.inspect}"

这将产生:

Original numbers: [1, 2, 3, 4, 5]
Squared numbers: [1, 4, 9, 16, 25]
Uppercased fruits: ["APPLE", "BANANA", "CHERRY"]

注意:虽然 collect 是一个有效的别名,但在现代 Ruby 代码中通常首选使用 map 以保持清晰和一致性。

当 map 用于 Hash 时,块会接收 [key, value] 对作为参数。它返回一个新数组。

#!/usr/bin/env ruby
scores = { "Alice" => 90, "Bob" => 75, "Charlie" => 82 }
descriptions = scores.map do |name, score|
"#{name} scored #{score} points."
end
puts descriptions.inspect
# If you want to transform a hash into another hash, use `transform_values` (Ruby 2.4+) or `map` followed by `to_h`.
# 如果想将哈希转换为另一个哈希,可以使用 `transform_values` (Ruby 2.4+) 或 `map` 后跟 `to_h`。
incremented_scores = scores.transform_values { |score| score + 5 }
puts "Incremented scores (Hash): #{incremented_scores.inspect}"
# Using map and to_h for more complex transformations resulting in a Hash
# 使用 map 和 to_h 进行更复杂的转换以生成哈希
student_data = scores.map do |name, score|
[name.to_sym, { score: score, status: score >= 80 ? "Pass" : "Fail" }]
end.to_h
puts "Student data (Hash): #{student_data.inspect}"

这将产生:

["Alice scored 90 points.", "Bob scored 75 points.", "Charlie scored 82 points."]
Incremented scores (Hash): {"Alice"=>95, "Bob"=>80, "Charlie"=>87}
Student data (Hash): {:Alice=>{:score=>90, :status=>"Pass"}, :Bob=>{:score=>75, :status=>"Fail"}, :Charlie=>{:score=>82, :status=>"Pass"}}

Enumerable 模块中的其他有用迭代器:

Section titled “Enumerable 模块中的其他有用迭代器:”
  • select(或 find_all):返回一个新数组,其中包含块返回真值的所有元素。
  • reject:与 select 相反。返回一个新数组,其中包含块返回假值或 nil 的所有元素。
  • find(或 detect):返回块返回真值的第一个元素。
  • reduce(或 inject):通过应用块或符号指定的二元操作来组合集合的所有元素。它非常强大,可用于求和、累积值等。
  • any?、all?、none?:检查是否有任何、所有或没有元素满足块给出的条件。
#!/usr/bin/env ruby
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = numbers.select { |n| n.even? }
puts "Even numbers: #{even_numbers.inspect}" #=> [2, 4, 6]
sum = numbers.reduce(0) { |accumulator, current_element| accumulator + current_element }
# Alternative: numbers.reduce(:+) works for simple sums
# 另一种方法:numbers.reduce(:+) 适用于简单的求和
puts "Sum of numbers: #{sum}" #=> 21
puts "Are there any numbers > 5? #{numbers.any? { |n| n > 5 }}" #=> true

迭代器在 Ruby 中随处可见。例如,处理用户输入、转换来自数据库或 API 的数据、过滤列表和生成报告。迭代器链允许优雅且可读的数据操作流程。

data = [{id: 1, name: "Alice", active: true}, {id: 2, name: "Bob", active: false}, {id: 3, name: "Charlie", active: true}]
# Get names of active users, uppercased
# 获取活跃用户的姓名,并转换为大写
active_user_names = data.select { |user| user[:active] }
.map { |user| user[:name].upcase }
puts "Active user names: #{active_user_names.inspect}" #=> ["ALICE", "CHARLIE"]

有关更多信息,请参阅 Ruby 文档中的 Enumerable 模块:https://ruby-doc.org/core/Enumerable.html