Skip to content

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
# 计数: 5

while循环的变体

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
end

loop循环的应用

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!"
end

upto和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}"
end

map/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}"  # 结果: 6

next语句

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
# 奇数: 9

redo语句

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次迭代, 计数器: 4

retry语句

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) }
end

4. 性能考虑

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学习之旅吧!

本站内容仅供学习和研究使用。