Ruby 循环语句
循环是编程中的基本控制结构,用于重复执行一段代码直到满足特定条件。Ruby提供了多种循环语句和迭代器,使代码能够高效地处理重复性任务。本章将详细介绍Ruby中的各种循环语句及其使用方法。
🔄 while循环
基本while循环
ruby
# 基本语法:while condition do ... end
counter = 0
while counter < 5
puts "计数: #{counter}"
counter += 1
end
# 输出:
# 计数: 0
# 计数: 1
# 计数: 2
# 计数: 3
# 计数: 4单行while循环
ruby
# 修饰符形式的while循环
counter = 0
puts "计数: #{counter += 1}" while counter < 5
# 输出:
# 计数: 1
# 计数: 2
# 计数: 3
# 计数: 4
# 计数: 5while循环的变体
ruby
# 使用break提前退出循环
counter = 0
while true
puts "计数: #{counter}"
counter += 1
break if counter >= 5
end
# 使用next跳过当前迭代
counter = 0
while counter < 10
counter += 1
next if counter.even? # 跳过偶数
puts "奇数: #{counter}"
end
# 输出:
# 奇数: 1
# 奇数: 3
# 奇数: 5
# 奇数: 7
# 奇数: 9🔁 until循环
基本until循环
ruby
# until是while的反向形式:until condition do ... end
counter = 0
until counter >= 5
puts "计数: #{counter}"
counter += 1
end
# 等同于: while counter < 5单行until循环
ruby
# 修饰符形式的until循环
counter = 0
puts "计数: #{counter += 1}" until counter >= 5
# 输出:
# 计数: 1
# 计数: 2
# 计数: 3
# 计数: 4
# 计数: 5🔁 loop循环
基本loop循环
ruby
# loop创建无限循环,必须使用break退出
counter = 0
loop do
puts "计数: #{counter}"
counter += 1
break if counter >= 5
endloop循环的应用
ruby
# 读取用户输入直到输入"quit"
loop do
print "请输入命令 (输入'quit'退出): "
input = gets.chomp
break if input == "quit"
puts "您输入了: #{input}"
end
# 重试机制
def fetch_data
attempts = 0
max_attempts = 3
loop do
begin
# 模拟网络请求
if rand > 0.3 # 70%成功率
return "数据获取成功"
else
raise "网络错误"
end
rescue => e
attempts += 1
if attempts >= max_attempts
puts "达到最大重试次数"
return nil
else
puts "重试第#{attempts}次..."
sleep 1
end
end
end
end🔢 数值循环
times循环
ruby
# 整数的times方法
5.times do |i|
puts "第#{i + 1}次迭代"
end
# 输出:
# 第1次迭代
# 第2次迭代
# 第3次迭代
# 第4次迭代
# 第5次迭代
# 单行形式
5.times { |i| puts "计数: #{i}" }
# 不使用索引参数
5.times do
puts "Hello!"
endupto和downto循环
ruby
# 从当前数到指定数
1.upto(5) do |i|
puts "向上计数: #{i}"
end
# 从当前数到指定数(向下)
10.downto(5) do |i|
puts "向下计数: #{i}"
end
# 单行形式
1.upto(3) { |i| puts "数字: #{i}" }
10.downto(8) { |i| puts "倒数: #{i}" }step循环
ruby
# 指定步长的循环
1.step(10, 2) do |i|
puts "步长为2: #{i}"
end
# 输出: 1, 3, 5, 7, 9
# 倒序步长
10.step(1, -2) do |i|
puts "倒序步长为2: #{i}"
end
# 输出: 10, 8, 6, 4, 2📦 集合迭代
each迭代器
ruby
# 数组迭代
fruits = ["苹果", "香蕉", "橙子"]
fruits.each do |fruit|
puts "水果: #{fruit}"
end
# 哈希迭代
person = {name: "张三", age: 25, city: "北京"}
person.each do |key, value|
puts "#{key}: #{value}"
end
# 单行形式
fruits.each { |fruit| puts fruit }
# 只迭代键或值
person.each_key { |key| puts key }
person.each_value { |value| puts value }each_with_index迭代器
ruby
# 同时获取元素和索引
fruits = ["苹果", "香蕉", "橙子"]
fruits.each_with_index do |fruit, index|
puts "第#{index + 1}个水果: #{fruit}"
end
# 哈希的each_with_index
person = {name: "张三", age: 25, city: "北京"}
person.each_with_index do |(key, value), index|
puts "第#{index + 1}项: #{key} = #{value}"
endmap/collect迭代器
ruby
# 转换数组元素
numbers = [1, 2, 3, 4, 5]
# map返回新数组
squared = numbers.map { |n| n * n }
puts squared.inspect # [1, 4, 9, 16, 25]
# 就地修改使用map!
numbers.map! { |n| n * 2 }
puts numbers.inspect # [2, 4, 6, 8, 10]
# 字符串处理示例
names = ["张三", "李四", "王五"]
capitalized = names.map { |name| name.upcase }
puts capitalized.inspect # ["张三", "李四", "王五"]select/reject迭代器
ruby
# 筛选元素
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]
# 拒绝奇数(等同于选择偶数)
evens = numbers.reject { |n| n.odd? }
puts evens.inspect # [2, 4, 6, 8, 10]
# 字符串筛选示例
words = ["apple", "banana", "cherry", "date"]
long_words = words.select { |word| word.length > 5 }
puts long_words.inspect # ["banana", "cherry"]find/detect迭代器
ruby
# 查找第一个匹配的元素
numbers = [1, 3, 5, 8, 9, 12]
# 查找第一个偶数
first_even = numbers.find { |n| n.even? }
puts first_even # 8
# 查找第一个长度大于5的单词
words = ["cat", "dog", "elephant", "bird"]
long_word = words.find { |word| word.length > 5 }
puts long_word # elephant
# 如果没找到,返回nil
no_match = numbers.find { |n| n > 20 }
puts no_match # nil
# 提供默认值
no_match_with_default = numbers.find(-> { "未找到" }) { |n| n > 20 }
puts no_match_with_default # 未找到🔧 高级迭代器
reduce/inject迭代器
ruby
# 归约操作
numbers = [1, 2, 3, 4, 5]
# 计算总和
sum = numbers.reduce(0) { |acc, n| acc + n }
puts sum # 15
# 简写形式
sum = numbers.reduce(:+)
puts sum # 15
# 计算乘积
product = numbers.reduce(1) { |acc, n| acc * n }
puts product # 120
# 查找最大值
max = numbers.reduce { |max, n| n > max ? n : max }
puts max # 5
# 字符串连接
words = ["Hello", " ", "World", "!"]
sentence = words.reduce("") { |acc, word| acc + word }
puts sentence # Hello World!group_by迭代器
ruby
# 按条件分组
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 按奇偶性分组
groups = numbers.group_by { |n| n.even? ? "偶数" : "奇数" }
puts groups
# {"奇数"=>[1, 3, 5, 7, 9], "偶数"=>[2, 4, 6, 8, 10]}
# 按长度分组字符串
words = ["cat", "dog", "elephant", "bird", "ant"]
by_length = words.group_by { |word| word.length }
puts by_length
# {3=>["cat", "dog"], 8=>["elephant"], 4=>["bird"], 3=>["ant"]}
# 注意:相同键会合并,所以实际结果是 {3=>["cat", "dog", "ant"], 8=>["elephant"], 4=>["bird"]}sort_by迭代器
ruby
# 按条件排序
students = [
{name: "张三", age: 20, score: 85},
{name: "李四", age: 19, score: 92},
{name: "王五", age: 21, score: 78}
]
# 按分数排序
by_score = students.sort_by { |student| student[:score] }
puts by_score.map { |s| s[:name] } # ["王五", "张三", "李四"]
# 按年龄排序(降序)
by_age = students.sort_by { |student| -student[:age] }
puts by_age.map { |s| s[:name] } # ["王五", "张三", "李四"]⚙️ 循环控制语句
break语句
ruby
# 提前退出循环
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 查找第一个大于5的数
result = numbers.each do |n|
if n > 5
puts "找到第一个大于5的数: #{n}"
break n # 返回n作为each方法的结果
end
end
puts "结果: #{result}" # 结果: 6next语句
ruby
# 跳过当前迭代
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 只处理奇数
numbers.each do |n|
next if n.even? # 跳过偶数
puts "奇数: #{n}"
end
# 输出:
# 奇数: 1
# 奇数: 3
# 奇数: 5
# 奇数: 7
# 奇数: 9redo语句
ruby
# 重新执行当前迭代
counter = 0
3.times do |i|
counter += 1
puts "第#{i + 1}次迭代, 计数器: #{counter}"
redo if counter < 3 && i == 1 # 在第二次迭代时重新执行
end
# 输出:
# 第1次迭代, 计数器: 1
# 第2次迭代, 计数器: 2
# 第2次迭代, 计数器: 3
# 第3次迭代, 计数器: 4retry语句
ruby
# 在异常处理中重试
attempts = 0
begin
attempts += 1
puts "尝试第#{attempts}次"
# 模拟可能失败的操作
if attempts < 3
raise "操作失败"
end
puts "操作成功"
rescue => e
puts "错误: #{e.message}"
retry if attempts < 5
puts "放弃重试"
end🧪 循环实践示例
数据处理管道
ruby
class DataProcessor
def self.process(data)
data
.select { |item| item[:active] } # 筛选活跃项
.sort_by { |item| -item[:priority] } # 按优先级排序
.map { |item| transform_item(item) } # 转换数据
.take(10) # 取前10项
end
private
def self.transform_item(item)
{
id: item[:id],
name: item[:name].upcase,
score: calculate_score(item),
processed_at: Time.now
}
end
def self.calculate_score(item)
(item[:value] * item[:weight]).round(2)
end
end
# 使用数据处理管道
raw_data = [
{id: 1, name: "项目A", value: 10, weight: 2.5, priority: 1, active: true},
{id: 2, name: "项目B", value: 15, weight: 1.8, priority: 3, active: true},
{id: 3, name: "项目C", value: 8, weight: 3.2, priority: 2, active: false},
{id: 4, name: "项目D", value: 12, weight: 2.1, priority: 4, active: true}
]
processed_data = DataProcessor.process(raw_data)
processed_data.each { |item| puts item }文件处理循环
ruby
class FileProcessor
def self.process_files(directory, pattern = "*.txt")
processed_count = 0
error_count = 0
Dir.glob(File.join(directory, pattern)).each_with_index do |file_path, index|
begin
puts "处理文件 #{index + 1}: #{File.basename(file_path)}"
# 读取文件内容
content = File.read(file_path, encoding: "UTF-8")
# 处理内容
processed_content = process_content(content)
# 保存处理结果
output_path = file_path.sub(/\.txt$/, "_processed.txt")
File.write(output_path, processed_content)
processed_count += 1
puts " ✓ 处理完成"
rescue => e
error_count += 1
puts " ✗ 处理失败: #{e.message}"
next # 继续处理下一个文件
end
end
puts "\n处理完成: #{processed_count}个成功, #{error_count}个失败"
end
private
def self.process_content(content)
# 示例处理:转换为大写并添加时间戳
processed = content.upcase
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
processed + "\n\n处理时间: #{timestamp}"
end
end
# 使用文件处理器(需要实际文件才能运行)
# FileProcessor.process_files("/path/to/directory")用户输入验证循环
ruby
class UserInputValidator
def self.get_valid_input(prompt, validation_proc, error_message)
loop do
print prompt
input = gets.chomp
if validation_proc.call(input)
return input
else
puts error_message
end
end
end
def self.get_number_in_range(min, max)
get_valid_input(
"请输入#{min}到#{max}之间的数字: ",
->(input) {
begin
num = Integer(input)
num >= min && num <= max
rescue ArgumentError
false
end
},
"输入无效,请输入#{min}到#{max}之间的整数"
).to_i
end
def self.get_email
get_valid_input(
"请输入邮箱地址: ",
->(input) { input.match?(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i) },
"邮箱格式不正确"
)
end
def self.get_choice(options)
puts "请选择:"
options.each_with_index do |option, index|
puts " #{index + 1}. #{option}"
end
choice = get_valid_input(
"请输入选择 (1-#{options.length}): ",
->(input) {
begin
num = Integer(input)
num >= 1 && num <= options.length
rescue ArgumentError
false
end
},
"请输入有效的选择"
).to_i
options[choice - 1]
end
end
# 使用输入验证器(交互式示例)
=begin
# 获取年龄
age = UserInputValidator.get_number_in_range(1, 120)
puts "您的年龄是: #{age}"
# 获取邮箱
email = UserInputValidator.get_email
puts "您的邮箱是: #{email}"
# 获取选择
choices = ["选项A", "选项B", "选项C"]
selected = UserInputValidator.get_choice(choices)
puts "您选择了: #{selected}"
=end🎯 循环最佳实践
1. 选择合适的循环方式
ruby
# 好的做法:根据场景选择循环方式
# 已知迭代次数:使用times
5.times { |i| puts "第#{i + 1}次" }
# 数值范围:使用upto/downto
1.upto(10) { |i| puts i }
# 集合遍历:使用each
[1, 2, 3].each { |item| puts item }
# 数据转换:使用map
squared = [1, 2, 3].map { |n| n * n }
# 条件筛选:使用select
evens = [1, 2, 3, 4, 5].select(&:even?)2. 避免无限循环
ruby
# 好的做法:确保循环有退出条件
counter = 0
while counter < 10
puts counter
counter += 1 # 确保计数器递增
end
# 使用迭代器而不是手动循环
# 不推荐:
i = 0
while i < array.length
process(array[i])
i += 1
end
# 推荐:
array.each { |item| process(item) }3. 合理使用循环控制语句
ruby
# 好的做法:清晰的控制流
def find_user(users, target_name)
users.each do |user|
return user if user[:name] == target_name
end
nil # 未找到
end
# 避免复杂的嵌套控制
# 不推荐:
users.each do |user|
if user[:active]
posts.each do |post|
if post[:author] == user[:name]
if post[:published]
# 复杂的处理逻辑
end
end
end
end
end
# 推荐: 分解复杂逻辑
def process_user_posts(user, posts)
return unless user[:active]
user_posts = posts.select { |post|
post[:author] == user[:name] && post[:published]
}
user_posts.each { |post| process_post(post) }
end4. 性能考虑
ruby
# 避免在循环中重复计算
# 不推荐:
array.each do |item|
expensive_calculation = calculate_something() # 每次都计算
process(item, expensive_calculation)
end
# 推荐:
expensive_calculation = calculate_something() # 只计算一次
array.each do |item|
process(item, expensive_calculation)
end
# 使用适当的方法而不是手动循环
# 不推荐:
sum = 0
numbers.each { |n| sum += n }
# 推荐:
sum = numbers.sum📚 下一步学习
掌握了Ruby循环语句后,建议继续学习:
继续您的Ruby学习之旅吧!