控制流
each/times/case。
控制流
这一课你将能做到
- 熟练使用 if/unless/case、while/until/for 处理分支与循环
- 用 each/times/map/select/inject 等迭代器写出地道的 Ruby 循环,而不是传统 for 循环
- 正确使用 break/next/redo/retry 控制迭代行为,并理解迭代器与循环的本质区别
为什么要学
控制流是任何语言的核心,但 Ruby 的控制流有自己的「味道」:除非必要,不用 for 循环;unless 后置让条件读起来像自然语言;迭代器(iterator)是 Ruby 的灵魂——Rails 的 find_each、Sidekiq 的批量处理,底层都是迭代器。控制流不熟,Ruby 代码能写,但写不地道。
人话:控制流是程序的「交通指挥」。传统的 if/while/for 是手动挡,Ruby 的迭代器是自动挡——你告诉它「对每个元素做什么」,而不是「怎么一步步做」。
先建立图像
控制流选择指南
┌──────────────────┬──────────────────────────┐
│ 场景 │ 推荐写法 │
├──────────────────┼──────────────────────────┤
│ 条件分支(2-3 个) │ if / unless / 三目 │
│ 条件分支(多个值) │ case-when │
│ 固定次数循环 │ 3.times { } │
│ 遍历集合 │ each / map / select / inject │
│ 条件循环 │ while / until / loop { } │
│ 提前退出迭代 │ break / next │
└──────────────────┴──────────────────────────┘
核心心法:Ruby 程序员首选迭代器,其次才是循环。
跟着做
步骤 1:if / unless / case
score = 85
# if-elsif-else
if score >= 90
puts "优秀"
elsif score >= 60
puts "及格"
else
puts "不及格"
end
# unless:if not 的语义化写法,否定条件更自然
unless score < 60
puts "通过了"
end
# 后置写法:一行时最简洁
puts "高分" if score >= 90
puts "继续加油" unless score >= 90
# case-when:多值匹配,区间、类型、正则都能用
grade = case score
when 90..100 then "A"
when 80...90 then "B"
when 60...80 then "C"
else "D"
end
puts "等级:#{grade}"
# case 还能按类型匹配
value = "hello"
desc = case value
when Integer then "整数"
when String then "字符串"
when Array then "数组"
else "其他"
end
puts desc # => 字符串
步骤 2:while / until / loop
# while:条件为真时循环
count = 0
while count < 3
puts "while: #{count}"
count += 1
end
# until:条件为假时循环(while not 的语义化)
until count >= 5
puts "until: #{count}"
count += 1
end
# loop:无限循环,配合 break 退出
number = 0
loop do
number += 1
puts number
break if number >= 3
end
# 后置修饰符:简洁但注意——do-while 语义!
num = 0
begin
num += 1
puts num
end while num < 2 # 先执行一次,再判断条件
步骤 3:迭代器——Ruby 的灵魂
# each:最基础的遍历
[1, 2, 3].each do |n|
puts "元素: #{n}"
end
# times:固定次数
3.times { |i| puts "第 #{i} 次" } # i 从 0 开始
# map:变换,返回新数组
squares = [1, 2, 3].map { |n| n * n }
puts squares.inspect # => [1, 4, 9]
# select / reject:过滤
evens = [1, 2, 3, 4, 5].select { |n| n.even? }
odds = [1, 2, 3, 4, 5].reject { |n| n.even? }
puts evens.inspect # => [2, 4]
puts odds.inspect # => [1, 3, 5]
# inject / reduce:累积
sum = [1, 2, 3, 4].inject(0) { |acc, n| acc + n }
puts sum # => 10
# 简写:传符号
puts [1, 2, 3, 4].inject(:+) # => 10
puts [1, 2, 3, 4].sum # => 10(Ruby 2.4+)
# each_with_index:带索引遍历
["a", "b", "c"].each_with_index do |item, idx|
puts "#{idx}: #{item}"
end
# each_with_object:边遍历边构造新对象
hash = ["a", "b"].each_with_object({}) do |letter, h|
h[letter] = letter.upcase
end
puts hash.inspect # => {"a"=>"A", "b"=>"B"}