switch 进阶
本教程共 93 篇 · 第 23 篇 · 更新于 2026-08-08 · 约 9 分钟阅读
本节目标:掌握区间匹配、元组匹配、值绑定和 where 子句,写出既能精确匹配又能灵活判断的 switch。
基础章节里,switch 只是拿一个值和几个固定值比。但这只是冰山一角。Swift 的 case 后面能写“模式”,范围、组合值甚至临时变量都能成为匹配条件。这一章把这几个高级玩法讲透,它们是 Swift 模式匹配体系的精华。
1-1 区间匹配
case 后面不仅能写单个值,还能写一个区间。这在“按数值范围分类”时极好用,比如把任意数字翻译成自然语言的数量词。
let approximateCount = 62
let countedThings = "土星的光环"
let naturalCount: String
switch approximateCount {
case 0:
naturalCount = "没有"
case 1..<5:
naturalCount = "几个"
case 5..<12:
naturalCount = "若干个"
case 12..<100:
naturalCount = "几十个"
case 100..<1000:
naturalCount = "几百个"
default:
naturalCount = "很多"
}
print("\(countedThings)有\(naturalCount)。")
62 落在 12..<100 这个半开区间里,于是 naturalCount 被赋值为“几十个”。区间是闭区间 ... 或半开区间 ..<,和你在运算符章节学的完全一致,这里直接用在匹配上,让一个 switch 同时搞定定性和定量判断。
Tip区间匹配让 switch 既能处理“离散值”也能处理“连续范围”。写的时候注意区间之间不要留缝,否则中间那段值会掉进
default;也不要重叠,否则后面的区间永远没机会命中。
1-2 元组匹配:一次看多个值
元组能打包多个值,switch 也能拿整个元组来匹配。元组里每个位置可以单独设条件,还能用下划线 _ 当通配符,表示“这个位置不管是啥都行”。
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) 在原点")
case (_, 0):
print("\(somePoint) 在 x 轴上")
case (0, _):
print("\(somePoint) 在 y 轴上")
case (-2...2, -2...2):
print("\(somePoint) 在方框内")
default:
print("\(somePoint) 在方框外")
}
(1, 1) 会被 case (-2...2, -2...2) 接住,因为它落在以原点为中心、边长 4 的方框里。注意下划线 _ 代表“任意值”,所以 (_, 0) 表示“y 为 0、x 随便”。
Swift 允许多个 case 在理论上都能匹配同一个值,这时谁写在前面就先用谁。(0, 0) 其实同时满足 (_, 0)、(0, _) 和自身,但因为它排第一,所以优先命中 case (0, 0)。这点和 if 链一样:把最具体的放前面,避免被宽泛模式抢走。
1-3 值绑定:把匹配到的值取出来用
匹配的同时,你常常想把值“抓”出来在分支里用。这叫值绑定(value binding)。在 case 里用 let 或 var 给某个位置起个临时名字,匹配成功后这个名字就指向对应的值。
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("在 x 轴上,x 值为 \(x)")
case (0, let y):
print("在 y 轴上,y 值为 \(y)")
case let (x, y):
print("在别处,位置 (\(x), \(y))")
}
(2, 0) 命中 case (let x, 0):y 固定为 0,x 是 2,于是 x 被绑定为 2 并在分支里打印。最后一个 case let (x, y) 用两个通配绑定接住所有剩余情况,因此这里连 default 都不需要——它已经穷尽了。
Note值绑定的临时常量只在当前
case的花括号内有效。出了这个分支就访问不到了。这和guard let绑定的变量能在块外使用正好相反,是 switch 与 guard 在作用域上的一个关键差别。
1-4 where 子句:给匹配加额外门槛
case 还能跟一个 where 子句,在基本模式匹配的基础上再加一道条件。只有模式匹配成功且 where 后的条件为 true,这个分支才真正命中。
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) 在直线 x == y 上")
case let (x, y) where x == -y:
print("(\(x), \(y)) 在直线 x == -y 上")
case let (x, y):
print("(\(x), \(y)) 就是个普通点")
}
(1, -1) 满足 x == -y,所以命中第二个分支。where 让你能在“形状对得上”之后再做精细筛选,比如判断点落在哪条对角线上。它把“结构匹配”和“数值判断”组合在一行里,非常紧凑。
1-5 复合 case 也能带值绑定
复合 case(用逗号分隔多个模式)如果包含值绑定,有个硬性要求:每个模式的绑定集合和类型必须一致。这样无论命中哪一个子模式,分支里的临时变量都一定有值、且类型相同。
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("在坐标轴上,距离原点 \(distance)")
default:
print("不在坐标轴上")
}
两个模式都把一个位置绑定到 distance,且都是 Int。所以分支体里可以放心使用 distance,不用管具体命中的是哪一个子模式。如果类型对不上,编译器会拒绝,因为它无法保证 distance 在所有路径下都有值。
1-6 顺带提一句:if case 和 for case
模式匹配不只活在 switch 里。if 也能用 case 当条件,专门做模式匹配而不是布尔判断:
let point = (12, 100)
if case (let x, 100) = point {
print("在 y=100 这条线上,x 为 \(x)")
}
循环里也有 for case,只遍历集合里“匹配某个模式”的元素。这些是模式匹配的延伸,理解本章的 case 写法后,它们一通百通。
Warning
for case和普通的for-in不同:只有匹配的元素才会进入循环体,其余直接跳过。写的时候别漏了case关键字,否则语法含义完全不同。另外switch里的值绑定变量不会泄漏到外层,这是初学者容易误以为能用的地方。
1-7 从“命令式判断”到“声明式匹配”
用过一堆 if-else 的人,刚接触模式匹配会有点不习惯。命令式写法是你亲自写“先判断这个、再判断那个”的步骤;声明式匹配是你直接把“我想要什么样的数据”写进 case,让编译器去比对。后者更短、更不容易漏、读起来也更像在描述数据本身。
比如判别一个点的位置时,与其写 if x==0 && y==0 ... else if x==0 ...,不如用一个 switch 把几种几何情况并排摆出来。当模式变复杂,声明式写法的优势会指数级放大。
1-8 一个综合例子
把元组、值绑定和 where 合在一起,能写出很紧凑又有表达力的判断:
let point = (x: 0, y: 5)
switch point {
case (0, 0):
print("原点")
case (let x, 0):
print("在 x 轴上,x=\(x)")
case (0, let y):
print("在 y 轴上,y=\(y)")
case let (x, y) where x == y:
print("在对角线 x==y 上")
case let (x, y):
print("普通点 (\(x), \(y))")
}
这个例子把“位置 + 几何关系”全装进一个 switch。注意匹配顺序:更具体的写在前面,通配的 case let (x, y) 兜底。写多了你会形成一种直觉——先列特殊,后写一般。
把区间、元组、值绑定、where 组合起来,switch 就成了 Swift 里最强大的匹配工具之一。面对复杂的数据判别,先想想能不能用一个 switch 表达清楚,往往比一堆 if 更优雅。
1-9 表达式形式还是语句形式
回顾基础章节提过,switch 既有“语句形式”也有“表达式形式”。简单说:如果你只是想“执行某些动作”,用语句形式 switch x { case ...: 做事 };如果你是想“算出一个值”,用表达式形式 let result = switch x { case ...: 值 } 更紧凑。两者匹配规则完全一样,区别只在结果怎么用。
一个实用判断:当每个分支都在给同一个变量赋不同的值时,表达式形式往往更干净,因为它把“赋值”这件事从每个分支里抽了出来,只写一次。反之,如果各分支做的事情五花八门(打印、修改多个变量、调用不同函数),语句形式读起来反而更自然。
再送你一个实用提醒:模式匹配里“哪个 case 写在前面”会直接影响结果,所以永远把更具体、更特殊的模式放在前面,把通配兜底(如 case let (x, y))放在最后。养成这个习惯,你的 switch 既不会漏,也不会被宽泛模式偷走本该命中的分支。多写几个例子,手感很快就来了。