条件语句
根据特定的条件执行特定的代码通常是十分有用的。当错误发生时,你可能想运行额外的代码;或者,当值太大或太小时,向用户
显示一条消息。要实现这些功能,你就需要使用条件语句。
Swift 提供两种类型的条件语句: if 语句和 switch 语句。通常,当条件较为简单且可能的情况很少时,使用 if 语句。⽽ switch
语句更适用于条件较复杂、有更多排列组合的时候。并且 switch 在需要用到模式匹配(pattern- matching)的情况下会更有用。
If
if 语句最简单的形式就是只包含一个条件,只有该条件为 true 时,才执行相关代码:
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
}
// 输出“It's very cold. Consider wearing a scarf.”
上面的例子会判断温度是否小于等于 32 华⽒度(水的冰点)。如果是,则打印一条消息;否则,不打印任何消息,继续执⾏ if 块后
⾯的代码。
当然, if 语句允许二选一执⾏,叫做 else 从句。也就是当条件为 false 时,执⾏ else 语句:
temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// 输出“It's not that cold. Wear a t-shirt.”
显然,这两条分支中总有一条会被执⾏。由于温度已升至 40 华氏度,不算太冷,没必要再围巾。因此, else 分⽀就被触发了。
你可以把多个 if 语句链接在一起,来实现更多分支:
temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// 输出“It's really warm. Don't forget to wear sunscreen.”
在上面的例子中,额外的 if 语句用于判断是不是特别热。而最后的 else 语句被保留了下来,用于打印既不冷也不热时的消息。
实际上,当不需要完整判断情况的时候,最后的 else 语句是可选的:
temperatureInFahrenheit = 72
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
}
在这个例子中,由于既不冷也不热,所以不会触发 if 或 else if 分支,也就不会打印任何消息。
Switch
switch 语句会尝试把某个值与若干个模式(pattern)进⾏匹配。根据第一个匹配成功的模式, switch 语句会执⾏对应的代码。当
有可能的情况较多时,通常用 switch 语句替换 if 语句。
switch 语句最简单的形式就是把某个值与一个或若干个相同类型的值作比较:
switch some value to consider {
case value 1:
respond to value 1
case value 2,
value 3:
respond to value 2 or 3
default:
otherwise, do something else
}
switch 语句由多个 case 构成,每个由 case 关键字开始。为了匹配某些更特定的值,Swift 提供了几种方法来进⾏更复杂的模式匹配,
这些模式将在本节的稍后部分提到。
与 if 语句类似,每一个 case 都是代码执⾏的一条分支。 switch 语句会决定哪一条分支应该被执⾏,这个流程被称作根据给定的
值切换(switching)。
switch 语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况
下,你可以使用默认( default )分支来涵盖其它所有没有对应的值,这个默认分支必须在 switch 语句的最后面。
下⾯的例子使用 switch 语句来匹配一个名为 someCharacter 的小写字符:
let someCharacter: Character = "z"
switch someCharacter {
case "a":
print("The first letter of the alphabet")
case "z":
print("The last letter of the alphabet")
default:
print("Some other character")
}
// 输出“The last letter of the alphabet”
在这个例子中,第一个 case 分支用于匹配第一个英文字母 a ,第二个 case 分支用于匹配最后一个字母 z 。因为switch 语句必
须有一个case 分支用于覆盖所有可能的字符,而不仅仅是所有的英文字母,所以 switch 语句使用default 分支来匹配除了 a 和 z
外的所有值,这个分支保证了 swith 语句的完备性。
不存在隐式的贯穿
与 C 和 Objective-C 中的 switch 语句不同,在 Swift 中,当匹配的 case 分支中的代码执⾏完毕后,程序会终⽌switch 语句,⽽
不会继续执⾏下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用 break 语句。这使得 switch 语句更安全、更易
用,也避免了漏写 break 语句导致多个语言被执⾏的错误。
注意
虽然在 Swift 中 break 不是必须的,但你依然可以在 case 分支中的代码执⾏完毕前使用 break 跳出。
每⼀个 case 分支都必须包含至少一条语句。像下面这样书写代码是⽆效的,因为第一个 case 分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // ⽆效,这个分支下面没有语句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 这段代码会报编译错误
不像 C 语言里的 switch 语句,在 Swift 中, switch 语句不会一起匹配 "a" 和 "A" 。相反的,上面的代码会引起编译期错误: case
"a": 不包含任何可执⾏语句 ——这就避免了意外地从一个 case 分支贯穿到另外一个,使得代码更安全、也更直观。
为了让单个 case 同时匹配 a 和 A ,可以将这个两个值组合成一个复合匹配,并且用逗号分开:
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 输出“The letter A”
为了可读性,符合匹配可以写成多⾏形式。
注意
如果想要显式贯穿 case 分支,请使用 fallthrough 语句
区间匹配
case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的⾃然语言格式:
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// 输出“There are dozens of moons orbiting Saturn.”
在上例中, approximateCount 在一个 switch 声明中被评估。每一个 case 都与之进⾏比较。因为approximateCount 落在了 12
到 100的区间,所以 naturalCount 等于 "dozens of" 值,并且此后的执行跳出了 switch 语句。
元组
我们可以使用元组在同一个 switch 语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线 ( _ )来匹配
所有可能的值。
下⾯的例子展示了如何使⽤一个 (Int, Int) 类型的元组来分类下图中的点 (x, y):
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// 输出“(1, 1) is inside the box”
在上面的例子中, switch 语句会判断某个点是否是原点 (0, 0),是否在红⾊的 x 轴上,是否在橘黄色的 y 轴上,是否在一个以原
点为中⼼的 4x4 的蓝色矩形里,或者在这个矩形外面。
不像 C 语言,Swift 允许多个 case 匹配同一个值。实际上,在这个例子中,点 (0, 0)可以匹配所有四个 case。但是,如果存在
多个匹配,那么只会执⾏第一个被匹配到的 case 分支。考虑点 (0, 0)会首先匹配 case (0, 0) ,因此剩下的能够匹配的分⽀都会
被忽视掉。
值绑定
case 分支允许将匹配的值声明为临时常量或变量,并且在 case 分支体内使用 —— 这种⾏为被称为值绑定(value binding),因
为匹配的值在 case 分支体内,与临时的常量或变量绑定。
下⾯的例子将下图中的点 (x, y),使用 (Int, Int) 类型的元组表示,然后分类表示:
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case let (x, y):
print("somewhere else at (\(x), \(y))")
}
// 输出“on the x-axis with an x value of 2”
在上⾯的例子中, switch 语句会判断某个点是否在红色的 x 轴上,是否在橘⻩色的 y 轴上,或者不在坐标轴上。
这三个 case 都声明了常量 x 和 y 的占位符,⽤于临时获取元组 anotherPoint 的一个或两个值。第一个 case —— case (let x, 0)
将匹配一个纵坐标为 0 的点,并把这个点的横坐标赋给临时的常量 x 。类似的,第二个 case —— case (0, let y) 将匹配一个横坐
标为 0 的点,并把这个点的纵坐标赋给临时的常量 y 。
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支⾥使用。在这个例子中,它们用于打印给定点的类型。
请注意,这个 switch 语句不包含默认分支。这是因为最后一个 case —— case let(x, y) 声明了一个可以匹配余下所有值的元
组。这使得 switch 语句已经完备了,因此不需要再书写默认分支。
Where
case 分支的模式可以使用 where 语句来判断额外的条件。
下⾯的例子把下图中的点 (x, y)进⾏了分类:
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// 输出“(1, -1) is on the line x == -y”
在上面的例子中, switch 语句会判断某个点是否在绿色的对角线 x == y 上,是否在紫色的对角线 x == -y 上, 或者不在对⻆线上。
这三个 case 都声明了常量 x 和 y 的占位符,⽤于临时获取元组 yetAnotherPoint 的两个值。这两个常量被用作where 语句的一
部分,从而创建一个动态的过滤器(filter)。当且仅当 where 语句的条件为 true 时,匹配到的 case 分⽀才会被执⾏。
就像是值绑定中的例子,由于最后一个 case 分支匹配了余下所有可能的值, switch 语句就已经完备了,因此不需要再书写默认
分支。
复合型 Cases
当多个条件可以使用同一种⽅法来处理时,可以将这几种可能放在同一个 case 后面,并且用逗号隔开。当 case 后⾯的任意一
种模式匹配的时候,这条分支就会被匹配。并且,如果匹配列表过长,还可以分行书写:
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// 输出“e is a vowel”
这个 switch 语句中的第一个 case,匹配了英语中的五个小写元音字母。相似的,第⼆个 case 匹配了英语中所有的⼩写辅音字
母。最终, default 分支匹配了其它所有字符。
复合匹配同样可以包含值绑定。复合匹配⾥所有的匹配模式,都必须包含相同的值绑定。并且每一个绑定都必须获取到相同类型
的值。这保证了,⽆论复合匹配中的哪个模式发生了匹配,分⽀体内的代码,都能获取到绑定的值,并且绑定的值都有一样的类
型。
let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
print("On an axis, \(distance) from the origin")
default:
print("Not on an axis")
}
// 输出“On an axis, 9 from the origin”
上⾯的 case 有两个模式: (let distance, 0) 匹配了在 x 轴上的值, (0, let distance) 匹配了在 y 轴上的值。 两个模式都绑定了
distance ,并且 distance 在两种模式下,都是整型——这意味着分支体内的代码,只要 case 匹配,都可以获取到 distance 值。