Ruby 控制流

控制流让程序能"做判断"和"重复干活"。本章讲 Ruby 的所有控制流结构——ifunlesscasewhileuntil 以及各种迭代器。Ruby 的迭代器方法非常丰富,实际项目里几乎不用 for 循环,这是它和别的语言最大的区别之一。

1. if/elsif/else

最基础的条件分支:

# 基础 if/elsif/else
score = 85

if score >= 90
  puts "优秀"
elsif score >= 80
  puts "良好"        # 这条会输出
elsif score >= 60
  puts "及格"
else
  puts "不及格"
end

注意是 elsif(少一个 e),不是 elseif、不是 else if。这是 Ruby 借鉴 Perl 的写法,新手写错率很高。

2. 后置 if 修饰符(Ruby 特色)

Ruby 允许把 if/unless 写在语句后面,这让代码非常像英文:

# 后置 if 修饰符:简单条件一行搞定
puts "成年" if age >= 18
puts "免费" if user.vip?

# 后置 unless:条件为 false 时执行(等于 if !)
puts "未成年" unless age >= 18
raise "名字不能为空" unless name

# 三元运算符:也属于条件分支
status = age >= 18 ? "成年" : "未成年"

实际项目里大量使用,典型场景:

原则:简单的一行条件用后置,复杂多分支用普通 if/end。

3. unless:if 的反面

unlessif ! 的语法糖,语义是"除非":

# unless = if 的反面:除非满足,否则执行
unless user.admin?
  puts "请登录管理员账号"
end

# 等价于
if !user.admin?
  puts "请登录管理员账号"
end

# 新手建议:unless 只用在"没有 else 分支"的简单情况
# 有 else 时用 if 更清晰
unless x
  puts "x 为 nil 或 false"
else
  puts "x 为真"   # ⚠️ 这种写法可读性差,改用 if
end

新手口诀:unless 只用在没有 else 的简单情况。一旦有 else 分支,改用 if !cond 更可读。

4. case/when(Ruby 的 switch)

多分支选择,比 if/elsif 链更清晰:

# case/when:多分支选择(类似其他语言的 switch)
grade = "B"

case grade
when "A"
  puts "优秀"
when "B"
  puts "良好"     # 这条
when "C"
  puts "及格"
when "D", "F"      # 多个值用逗号
  puts "不及格"
else
  puts "未知"
end

# case 还能省略目标,直接写条件
age = 25
case
when age < 18
  puts "未成年"
when age < 60
  puts "青年"      # 这条
else
  puts "老年"
end

Ruby 的 case 比其他语言的 switch 强大,因为它的匹配用的是 === 运算符,而 Ruby 里 ===被各类型重载的:

所以 case 能优雅地配合范围、类型、正则:

# case 配合 Range 很优雅
score = 85
case score
when 90..100
  puts "优秀"
when 80..89
  puts "良好"      # 这条
when 60..79
  puts "及格"
when 0..59
  puts "不及格"
end

# case 判断类型(=== 机制)
case "hello"
when String
  puts "是字符串"
when Integer
  puts "是整数"
end

5. while 循环

# while:先判条件再循环
i = 0
while i < 5
  puts i
  i += 1
end
# 输出 0 1 2 3 4

# begin/end while:至少执行一次(类似 do/while)
count = 0
begin
  puts count
  count += 1
end while count < 3

begin/end while 是 Ruby 版的 do/while——先执行一次再判断条件。这种写法在实际项目里很少见,大家更倾向用 loopbreak

6. until 循环

until 是 while 的反面——直到条件为真才停:

# until:while 的反面,直到条件为真才停
i = 5
until i == 0
  puts i
  i -= 1
end
# 输出 5 4 3 2 1

# 后置 until
puts "等待中" until ready

7. loop 与 break/next

loop 是个无限循环,必须用 break 退出:

# loop 是 Kernel 方法:无限循环,必须靠 break 退出
i = 0
loop do
  break if i >= 5
  puts i
  i += 1
end

# 配合 next 跳过本次
5.times do |n|
  next if n.even?
  puts n          # 只输出奇数:1 3
end

# redo:重新执行本次(慎用)

三个循环控制关键字:

8. 迭代器:Ruby 的循环主力

Ruby 社区几乎不用 for,而是用迭代器方法——它们更优雅、更接近"描述要做什么"而非"怎么做":

# Ruby 社区几乎不用 for,而是用迭代器方法

# times:重复 n 次
3.times { |i| puts i }       # 0 1 2

# upto / downto:从某数到某数
1.upto(5) { |i| puts i }     # 1 2 3 4 5
5.downto(1) { |i| puts i }   # 5 4 3 2 1

# each:遍历集合(最常用)
[10, 20, 30].each { |x| puts x }

# step:按步长
0.step(10, 2) { |i| puts i }   # 0 2 4 6 8 10

# for 写法(也支持但少用)
for x in [1, 2, 3] do
  puts x
end

为什么不用 for?因为 for 在 Ruby 里其实只是 each 的语法糖,且不创建新作用域(for 里定义的变量在外面也能访问),容易出 bug。所以社区一致用迭代器。

9. 谁优先:迭代器 vs 循环?

90% 的循环场景,都能用迭代器更优雅地解决。这是 Ruby 与 C/Java 最大的"思维差异"。

小结

本章你学会了 Ruby 全部控制流结构。重点是理解迭代器优先于循环——这是 Ruby 的核心哲学之一。下一篇我们看方法,把"一段可复用的逻辑"封装起来。

← 上一篇 Ruby 运算符

下一篇 Ruby 方法

✈️💬