第5天 条件语句、Switch 语句和三元运算符
如果说莎士比亚的《哈姆雷特》中有一句台词是大多数人都知道的,那就是 “生存还是毁灭,这是一个问题”, 莎士比亚想用这句话表达关于生与死的终极问题,但巧合的是,这句话恰好触及了编程逻辑的核心:判断一个条件是真还是假。
今天我们将深入了解 Swift 的一个重要部分:运算符和条件语句,它们能让我们在程序运行时评估程序的状态,并根据结果采取不同的操作。实现这一功能有多种方法,你需要全部掌握,不过我们会一步一步讲解,以便你了解它们之间的区别。
今天你需要完成四个教程,在这些教程中,你会接触到 if、switch 等内容。 你可以选择性地阅读补充材料,之后还有一个小测试,帮助你确认自己是否理解了所学内容。
5.1 如何检查条件为真还是为假
作者:Paul Hudson 2021 年 10 月 25 日 已针对 Xcode 16.4 更新
程序经常需要做出 “选择”:
- 如果学生的考试分数超过 80 分,就打印一条成功消息。
- 如果用户输入的名字在字母顺序上排在其朋友的名字之后,就把朋友的名字放在前面。
- 如果向数组中添加一个数字后,数组包含的元素超过 3 个,就移除最旧的那个元素。
- 如果要求用户输入名字但用户什么都没输入,就给他们一个默认名字 “Anonymous”(匿名)。
Swift 通过if语句来处理这些情况,if语句能让我们检查一个条件,当条件为真时执行一些代码。其形式如下:
if someCondition {
print("做某事")
}我们来详细分析一下:
- 条件以
if开头,这向 Swift 表明我们要在代码中检查某种条件。 someCondition部分是编写条件的地方 —— 分数是否超过 80 分?数组是否包含超过 3 个元素?- 如果条件为真 —— 比如分数确实超过了 80 分 —— 那么我们就会打印 “做某事” 这条消息。
当然,代码中不止这些内容:我还没提到{和}这两个符号。它们被称为 “花括号”—— 更具体地说,是左花括号和右花括号 —— 不过有时你也会听到它们被叫做 “大括号”。
在 Swift 中,这些花括号被广泛用于标记代码块:左花括号开始一个代码块,右花括号结束一个代码块。代码块内部是当我们检查的条件为真时想要运行的所有代码,在我们的例子中就是打印一条消息。
你可以在代码块中包含任意多的代码:
if someCondition {
print("做某事")
print("做另一件事")
print("做第三件事")
}当然,真正重要的是someCondition部分,因为这是你编写检查代码的地方:你实际想要检查的条件是什么?
我们来试试分数的例子:如果一个score常量大于 80,我们就打印一条消息。代码如下:
let score = 85
if score > 80 {
print("做得好!")
}在这段代码中,score > 80就是我们的条件。你会记得在学校里>的意思是 “大于”,所以我们的完整条件是 “如果分数大于 80”。如果分数确实大于 80,就会打印 “做得好!”—— 很好!
>这个符号是一个比较运算符,因为它比较两个事物并返回一个布尔结果:左边的事物是否大于右边的事物?你还可以使用<表示小于,>=表示 “大于或等于”,<=表示 “小于或等于”。
我们来试一下 —— 你认为这段代码会打印什么?
let speed = 88
let percentage = 85
let age = 18
if speed >= 88 {
print("我们要去的地方不需要 roads( roads 此处指普通道路,结合语境可理解为“常规道路”)。")
}
if percentage < 85 {
print("抱歉,你没通过考试。")
}
if age >= 18 {
print("你有资格投票")
}试着在脑子里运行一下这段代码 —— 哪些print()语句会实际执行?
第一个print()语句会在speed大于或等于 88 时执行,因为speed正好是 88,所以第一个print()代码会运行。
第二个print()语句会在percentage小于 85 时执行,而percentage正好是 85,所以第二个print()不会运行 —— 我们用的是小于,而不是小于或等于。
第三个print()语句会在age大于或等于 18 时执行,因为age正好是 18,所以第三个print()会运行。
现在我们来试试第二个示例条件:如果用户输入的名字在字母顺序上排在其朋友的名字之后,就把朋友的名字放在前面。你已经知道<、>=等运算符在处理数字时很有用,但它们在处理字符串时同样也能很好地工作:
let ourName = "Dave Lister"
let friendName = "Arnold Rimmer"
if ourName < friendName {
print("是 \(ourName) 对 \(friendName)")
}
if ourName > friendName {
print("是 \(friendName) 对 \(ourName)")
}所以,如果ourName中的字符串在按字母顺序排序时位于friendName中的字符串之前,就会先打印ourName,再打印friendName,完全符合我们的需求。
我们来看第三个示例条件:如果向数组中添加一个数字后,数组包含的元素超过 3 个,就移除最旧的那个元素。你已经学过append()、count和remove(at:),现在我们可以把这三个结合起来,形成这样的条件:
// 创建一个包含3个数字的数组
var numbers = [1, 2, 3]
// 添加第4个数字
numbers.append(4)
// 如果数组中的元素超过3个
if numbers.count > 3 {
// 移除最旧的数字
numbers.remove(at: 0)
}
// 显示结果
print(numbers)现在来看第四个示例条件:如果要求用户输入名字但用户什么都没输入,就给他们一个默认名字 “Anonymous”。
要解决这个问题,你首先需要了解另外两个会经常用到的比较运算符,它们都用于处理相等性。第一个是==,表示 “等于”,用法如下:
let country = "Canada"
if country == "Australia" {
print("G'day!(澳大利亚问候语,可译为“你好!”)")
}第二个是!=,表示 “不等于”,用法如下:
let name = "Taylor Swift"
if name != "Anonymous" {
print("欢迎,\(name)")
}在我们的例子中,我们想要检查用户输入的用户名是否为空,我们可以这样做:
// 创建用户名变量
var username = "taylorswift13"
// 如果`username`包含空字符串
if username == "" {
// 把它设为“Anonymous”
username = "Anonymous"
}
// 现在打印欢迎消息
print("欢迎,\(username)!")""是空字符串:我们开始一个字符串然后立即结束它,中间什么都没有。通过将username与空字符串进行比较,我们就是在检查用户是否输入了空字符串作为用户名,这正是我们想要的。
不过,还有其他方法可以进行这种检查,理解它们的作用很重要。
首先,我们可以将字符串的count(即它包含的字母数量)与 0 进行比较,如下所示:
if username.count == 0 {
username = "Anonymous"
}在任何语言中,比较两个字符串都不是很快,所以我们用整数比较代替了字符串比较:字符串的字母数量是否等于 0?
在很多语言中,这种方法速度很快,但在 Swift 中并非如此。你看,Swift 支持各种复杂的字符串 —— 实际上,它支持所有人类语言,包括表情符号,这在很多其他编程语言中是做不到的。然而,这种强大的支持是有代价的,其中一个代价就是,当你要获取一个字符串的count时,Swift 需要逐个遍历并数出所有的字母 —— 它不会将字符串的长度与字符串本身分开存储。
所以,想想这种情况:你有一个非常长的字符串,里面存储了莎士比亚的全部作品。我们检查count == 0时,需要遍历并数出字符串中所有的字母,尽管只要数到至少一个字符,我们就知道这个问题的答案了。
因此,Swift 为所有的字符串、数组、字典和集合增加了第二个功能:isEmpty。如果你检查的对象内部没有任何内容,它就会返回true,我们可以用它来改进我们的条件,如下所示:
if username.isEmpty == true {
username = "Anonymous"
}这样更好了,但我们还可以更进一步。你看,归根结底,重要的是你的条件必须最终是真或假;Swift 不允许其他情况。在我们的例子中,username.isEmpty本身就是一个布尔值,也就是说它不是真就是假,所以我们可以让代码更简洁:
if username.isEmpty {
username = "Anonymous"
}如果isEmpty为真,条件就成立,username会被设为 “Anonymous”;否则,条件不成立。
【可选阅读】Swift 如何让我们比较多种类型的数据?
作者:Paul Hudson 2021 年 10 月 25 日 已针对 Xcode 16.4 更新
Swift 允许我们直接比较多种类型的值,这意味着我们可以检查各种值的相等性和大小关系。例如,如果我们有这样一些值:
let firstName = "Paul"
let secondName = "Sophie"
let firstAge = 40
let secondAge = 10那么我们可以通过多种方式来比较它们:
print(firstName == secondName)
print(firstName != secondName)
print(firstName < secondName)
print(firstName >= secondName)
print(firstAge == secondAge)
print(firstAge != secondAge)
print(firstAge < secondAge)
print(firstAge >= secondAge)在幕后,Swift 以一种非常巧妙的方式实现了这一点,这使得它能够比较各种各样的事物。例如,Swift 有一种专门用于存储日期的类型叫做Date,你可以使用相同的运算符来比较日期:比如someDate < someOtherDate。
我们甚至可以让 Swift 使我们的枚举具有可比性,像这样:
enum Sizes: Comparable {
case small
case medium
case large
}
let first = Sizes.small
let second = Sizes.large
print(first < second)这会打印 “true”,因为small在枚举的 case 列表中排在large之前。
5.2 如何检查多个条件
作者:Paul Hudson 2021 年 11 月 24 日 已针对 Xcode 16.4 更新
当我们使用 if 时,必须给 Swift 提供一种条件,这种条件在被评估后要么为真,要么为假。如果你想检查几个不同的值,可以像这样一个接一个地放置它们:
let age = 16
if age >= 18 {
print("你可以在下一次选举中投票。")
}
if age < 18 {
print("抱歉,你太年轻了,还不能投票。")
}然而,仔细想想,这并不是很高效:我们的两个条件是互斥的,因为如果某人大于或等于 18 岁(第一个条件),那么他们就不可能小于 18 岁(第二个条件),反之亦然。我们这是在让 Swift 做不必要的工作。
在这种情况下,Swift 为我们提供了一种更高级的条件,允许我们在代码中添加一个 else 块 —— 当条件为假时运行的一些代码。
使用 else,我们可以把前面的代码重写成这样:
let age = 16
if age >= 18 {
print("你可以在下一次选举中投票。")
} else {
print("抱歉,你太年轻了,还不能投票。")
}现在 Swift 只需要检查 age 一次:如果它大于或等于 18,就运行第一个 print() 代码;但如果它是任何小于18 的值,就运行第二个 print() 代码。
所以,现在我们的条件看起来是这样的:
if someCondition {
print("如果条件为真,这部分代码将运行")
} else {
print("如果条件为假,这部分代码将运行")
}还有一种更高级的条件叫做 else if,它允许你在第一个检查失败时运行一个新的检查。你可以根据需要只使用一个 else if,或者使用多个 else if,甚至可以在需要时将 else if 和 else 结合起来。不过,你只能有一个 else,因为它的意思是 “如果所有其他条件都为假”。
它的样子如下:
let a = false
let b = true
if a {
print("如果a为真,运行这段代码")
} else if b {
print("如果a为假但b为真,运行这段代码")
} else {
print("如果a和b都为假,运行这段代码")
}你可以根据需要继续添加更多的 else if 条件,但要注意你的代码不要变得太复杂!
除了使用 else 和 else if 来构建更复杂的条件外,你还可以检查不止一件事情。例如,我们可能想说 “如果今天的温度超过 20 摄氏度但低于 30 摄氏度,就打印一条消息”。
这有两个条件,所以我们可以这样写:
let temp = 25
if temp > 20 {
if temp < 30 {
print("今天天气不错。")
}
}虽然这样也能很好地工作,但 Swift 提供了一个更简洁的替代方案:我们可以使用 && 来组合两个条件,只有当条件中的两个部分都为真时,整个条件才为真。
所以,我们可以把代码改成这样:
if temp > 20 && temp < 30 {
print("今天天气不错。")
}你应该把 && 理解为 “并且”,所以我们的整个条件读作 “如果温度大于 20 并且温度小于 30,就打印一条消息”。它被称为逻辑运算符,因为它结合布尔值来生成一个新的布尔值。
&& 有一个对应的符号是两个竖线 ||,意思是 “或者”。&& 只有在两个子条件都为真时,才能使一个条件为真,而 || 只要其中一个子条件为真,就能使一个条件为真。
例如,我们可以说,用户可以购买一款游戏,如果他们至少 18 岁,或者如果他们未满 18 岁,他们必须获得父母的许可。我们可以使用 || 这样写:
let userAge = 14
let hasParentalConsent = true
if userAge >= 18 || hasParentalConsent == true {
print("你可以购买这款游戏")
}这会打印 “你可以购买这款游戏”,因为虽然我们条件的前半部分不成立 —— 用户还不到 18 岁 —— 但后半部分成立,因为他们确实有父母的许可。
记住,在条件中使用 == true 是可以省略的,因为我们显然已经在检查一个布尔值了。所以,我们可以改成这样写:
if userAge >= 18 || hasParentalConsent {
print("你可以购买这款游戏")
}为了总结检查多个条件的内容,让我们尝试一个更复杂的例子,它同时结合了 if、else if、else 和 ||,甚至展示了枚举在条件中的应用。
在这个例子中,我们将创建一个名为 TransportOption 的枚举,它包含五个情况:airplane(飞机)、helicopter(直升机)、bicycle(自行车)、car(汽车)和 scooter(滑板车)。然后我们给一个常量赋值一个示例值,并进行一些检查:
- 如果我们要乘飞机或直升机去某个地方,我们会打印 “让我们飞吧!”
- 如果我们骑自行车去,我们会打印 “希望有自行车道……”
- 如果我们开车去,我们会打印 “该陷入交通堵塞了。”
- 否则我们会打印 “我现在要租一辆滑板车!”
代码如下:
enum TransportOption {
case airplane, helicopter, bicycle, car, scooter
}
let transport = TransportOption.airplane
if transport == .airplane || transport == .helicopter {
print("让我们飞吧!")
} else if transport == .bicycle {
print("希望有自行车道……")
} else if transport == .car {
print("该陷入交通堵塞了。")
} else {
print("我现在要租一辆滑板车!")
}我想强调一下这段代码的几个部分:
- 当我们给
transport设置值时,需要明确我们指的是TransportOption.airplane。我们不能只写.airplane,因为 Swift 不知道我们指的是TransportOption枚举。 - 一旦完成赋值,我们就不需要再写
TransportOption了,因为 Swift 知道transport一定是某种TransportOption类型。所以,我们可以检查它是否等于.airplane,而不是TransportOption.airplane。 - 这段代码使用
||来检查transport是等于.airplane还是 等于.helicopter,如果其中任何一个为真,那么条件就为真,并打印 “让我们飞吧!”。 - 如果第一个条件不成立 —— 如果交通方式不是
.airplane或.helicopter—— 那么就会运行第二个条件:交通方式是.bicycle吗?如果是,就打印 “希望有自行车道……”。 - 如果我们也不是骑自行车去,那么我们会检查是否是开车去。如果是,就打印 “该陷入交通堵塞了。”。
- 最后,如果所有前面的条件都不成立,那么就会运行
else块,这意味着我们要乘滑板车去。
【可选阅读】if 和 else if 有什么区别?
作者:Paul Hudson 2020 年 5 月 28 日 已针对 Xcode 16.4 更新
当你刚开始学习 Swift 时,可能不太清楚什么时候用 else,什么时候用 else if,以及它们之间真正的区别是什么。
好吧,让我们从一个可以操作的示例值开始:
let score = 9001如果你好奇的话,没错,这源于《龙珠 Z》的梗。
我们可以写一个简单的条件来检查分数是否超过 9000,像这样:
if score > 9000 {
print("超过 9000 了!")
}现在,如果我们想为等于或低于 9000 的分数打印不同的消息,我们可以这样写:
if score > 9000 {
print("超过 9000 了!")
}
if score <= 9000 {
print("没超过 9000 !")
}这完全可行,你的代码会完全按照你的预期执行。但这样一来,Swift 就要做更多的工作:它需要检查 score 的值两次。对于简单的整数来说,这很快,但如果我们的数据更复杂,速度就会变慢。
这就是 else 的用处,因为它的意思是 “如果我们检查的条件不为真,就运行这段代码”。
所以,我们可以把之前的代码改写成这样:
if score > 9000 {
print("超过 9000 了!")
} else {
print("没超过 9000 !")
}这样一改,Swift 只会检查 score 一次,而且我们的代码更短,也更容易阅读。
现在假设我们想要三条消息:一条用于分数超过 9000 的情况,一条用于正好是 9000 的情况,还有一条用于低于 9000 的情况。我们可以这样写:
if score > 9000 {
print("超过 9000 了!")
} else {
if score == 9000 {
print("正好是 9000 !")
} else {
print("没超过 9000 !")
}
}同样,这完全没问题,而且效果和你期望的一样。不过,我们可以用 else if 让代码更容易阅读,它能让我们把紧跟在 else 后面的 if 直接组合起来,就像这样:
if score > 9000 {
print("超过 9000 了!")
} else if score == 9000 {
print("正好是 9000 !")
} else {
print("没超过 9000 !")
}为了演示,我想使用 Swift 的 print() 函数:传入一些文本运行它,文本就会被打印出来。
这让我们的代码更容易阅读和理解,因为我们看到的不是嵌套的条件,而是一个可以向下依次阅读的流程。
你可以有任意多个 else if 检查,但必须有且仅有一个 if,并且可以有零个或一个 else。
【可选阅读】如何检查多个条件
作者:Paul Hudson 2021 年 10 月 25 日 已针对 Xcode 16.4 更新
Swift 为我们提供了 && 和 || 来同时检查多个条件,当只用于两个条件时,它们相当直观。
例如,假设我们运营一个论坛,用户可以发布消息,并且可以删除自己拥有的任何消息。我们可能会写出这样的代码:
if isOwner == true || isAdmin == true {
print("你可以删除这条帖子")
}当我们想检查多个条件时,事情就会变得更复杂。例如,我们可以规定普通用户只有在获得允许的情况下才能删除消息,而管理员总是可以删除帖子。我们可能会写出这样的代码:
if isOwner == true && isEditingEnabled || isAdmin == true {
print("你可以删除这条帖子")
}但这是想检查什么呢?&& 和 || 的检查顺序是怎样的?它可能意味着这样:
if (isOwner == true && isEditingEnabled) || isAdmin == true {
print("你可以删除这条帖子")
}这表示 “如果我们是帖子的所有者并且编辑功能已启用,你可以删除帖子;或者如果你是管理员,即使你不拥有该帖子,也可以删除它。” 这是合理的:如果允许编辑,人们可以删除自己的帖子,但管理员总是可以删除帖子。
然而,你也可能会这样理解:
if isOwner == true && (isEditingEnabled || isAdmin == true) {
print("你可以删除这条帖子")
}现在它的意思就完全不同了:“如果你是帖子的所有者,并且要么编辑功能已启用,要么你是管理员,那么你可以删除帖子。” 这意味着管理员不能删除他们不拥有的帖子,这是不合理的。
显然,Swift 不喜欢这种歧义,所以它总会把代码解释成我们写了这样的形式:
if (isOwner == true && isEditingEnabled) || isAdmin == true {
print("你可以删除这条帖子")
}不过,说实话,让 Swift 来解决这个问题并不是一种好的体验,这就是为什么我们可以自己加上括号来明确我们的确切意思。
关于这一点没有具体的建议,但实际上,只要在单个条件中混合使用 && 和 ||,几乎都应该使用括号来使结果清晰明了。
【练习题】条件判断
问题 1/12:这段代码会打印 “Success”—— 对还是错?
var city = "Tokyo"
if city == "Madrid" {
print("Failure")
} else {
print("Success")
}问题 2/12:这段代码会打印 “Success”—— 对还是错?
let sharkCount: Int = 21
if sharkCount < 21 {
print("Success")
} else {
print("Failure")
}问题 3/12:这段代码会打印 “Success”—— 对还是错?
let passwordLength = 5
if passwordLength <= 5 {
print("Failure")
} else {
print("Success")
}问题 4/12:这段代码会打印 “Success”—— 对还是错?
var favoriteColor = "Red"
if favoriteColor == "red" {
print("Success")
} else {
print("Failure")
}问题 5/12:这段代码会打印 “Success”—— 对还是错?
var actualWage: Int = 22_000
var medianWage: Double = 22_000
if actualWage >= medianWage {
print("Success")
}问题 6/12:这段代码会打印 “Success”—— 对还是错?
var cupsOfCoffee = "4"
if cupsOfCoffee >= 3 {
print("Success")
} else {
print("Failure")
}问题 7/12:这段代码会打印 “Success”—— 对还是错?
let isAvailable: Bool = false
if isAvailable {
print("Success")
} else {
print("Failure")
}问题 8/12:这段代码会打印 “Success”—— 对还是错?
let age = 21
if age > 18 {
print("Success")
} else {
print("Failure")
}问题 9/12:这段代码会打印 “Success”—— 对还是错?
let employeeCount = 50
if employeeCount == 50 {
print("Success")
}问题 10/12:这段代码会打印 “Success”—— 对还是错?
let orderAmount = 200
if orderAmount >= 200 {
print("Success")
}问题 11/12:这段代码会打印 “Success”—— 对还是错?
let name = "Taylor"
if name == "Paul" {
print("Failure")
} else {
print("Success")
}问题 12/12:这段代码会打印 “Success”—— 对还是错?
let teaStrength = 5
if teaStrength >= 4 {
print("Success")
} else {
print("Failure")
}【练习题】组合条件
问题 1/12:这段代码会打印一条消息 —— 对还是错?
let a = 10
let b = 10
if a >= 10 && b <= 10 {
print("Hello, Swift!")
}问题 2/12:这段代码会打印一条消息 —— 对还是错?
let average1 = 5.0
let average2 = 4.0
if average1 > 5.0 && average2 > 4.0 {
print("Hello, Swift!")
}问题 3/12:这段代码会打印一条消息 —— 对还是错?
let a = true
let b = true
if a && b {
print("Hello, Swift!")
}问题 4/12:这段代码会打印一条消息 —— 对还是错?
let test = false
if test == false {
print("Hello, Swift!")
}问题 5/12:这段代码会打印一条消息 —— 对还是错?
let loggedIn = true
let authorized = false
if loggedIn && authorized {
print("Hello, Swift!")
}问题 6/12:这段代码会打印一条消息 —— 对还是错?
let rating = "5"
if rating > 4 {
print("Hello, Swift!")
}问题 7/12:这段代码会打印一条消息 —— 对还是错?
let age1 = 18
let age2 = 21
if age1 > 18 || age1 < 18 {
print("Hello, Swift!")
}问题 8/12:这段代码会打印一条消息 —— 对还是错?
let name1 = "Charlotte"
let name2 = "Sophie"
if name1 == "Paul" || name2 == "Sophie" {
print("Hello, Swift!")
}问题 9/12:这段代码会打印一条消息 —— 对还是错?
let a = true
let b = false
if a || b {
print("Hello, Swift!")
}问题 10/12:这段代码会打印一条消息 —— 对还是错?
let score1 = 23
let score2 = 18
if score1 > 18 | score2 < 18 {
print("Hello, Swift!")
}问题 11/12:这段代码会打印一条消息 —— 对还是错?
let age1 = 18
let age2 = 21
if age1 >= 18 || age1 <= 18 {
print("Hello, Swift!")
}问题 12/12:这段代码会打印一条消息 —— 对还是错?
let loggedIn = true
let authorized = false
if loggedIn == false && authorized == false {
print("Hello, Swift!")
}5.3 如何使用 switch 语句检查多个条件
作者:Paul Hudson 2024 年 4 月 11 日 已针对 Xcode 16.4 更新
你可以反复使用 if 和 else if 来根据需要多次检查条件,但这样读起来会有点费劲。例如,如果我们有一个来自枚举的天气预报,我们可以根据一系列条件选择要打印的消息,就像这样:
enum Weather {
case sun, rain, wind, snow, unknown
}
let forecast = Weather.sun
if forecast == .sun {
print("今天应该是个好天气。")
} else if forecast == .rain {
print("带上雨伞。")
} else if forecast == .wind {
print("穿点暖和的衣服")
} else if forecast == .rain {
print("学校停课了。")
} else {
print("我们的预报生成器坏了!")
}这虽然能工作,但存在一些问题:
- 我们不得不反复写
forecast,尽管我们每次检查的都是同一个东西。 - 我不小心检查了两次
.rain,尽管第二次检查永远不可能为真,因为只有第一次检查失败时才会执行第二次检查。 - 我根本没有检查
.snow,所以我们遗漏了相关功能。
我们可以使用另一种检查条件的方式 ——switch 来解决这三个问题。它也能让我们逐个检查各个情况,而且 Swift 还能提供帮助。对于枚举,它知道枚举可能有的所有情况,所以如果我们遗漏了一种情况或者重复检查了一种情况,它会发出警告。
因此,我们可以用下面的代码替换所有那些 if 和 else if 检查:
switch forecast {
case .sun:
print("今天应该是个好天气。")
case .rain:
print("带上雨伞。")
case .wind:
print("穿点暖和的衣服")
case .snow:
print("学校停课了。")
case .unknown:
print("我们的预报生成器坏了!")
}让我们来详细分析一下:
- 我们以
switch forecast开始,这告诉 Swift 这是我们要检查的值。 - 然后有一系列
case语句,每个语句都是我们要与forecast进行比较的值。 - 我们的每个 case 都列出了一种天气类型,而且因为我们是基于
forecast进行切换的,所以不需要写Weather.sun、Weather.rain等等 ——Swift 知道它一定是某种Weather类型。 - 在每个 case 之后,我们写一个冒号来标记如果该 case 匹配时要运行的代码的开始。
- 我们用一个右大括号来结束
switch语句。
如果你尝试把 .snow 改成 .rain,你会看到 Swift 会强烈警告:一次是我们检查了两次 .rain,另一次是我们的 switch 语句不是详尽的—— 它没有处理所有可能的情况。
如果你用过其他编程语言,你可能会注意到 Swift 的 switch 语句在两个地方有所不同:
- 所有的
switch语句必须是详尽的,这意味着必须处理所有可能的值,这样你就不会意外漏掉某个值。 - Swift 会执行第一个与你要检查的条件相匹配的 case,仅此而已。其他语言通常会继续执行后续所有 case 中的代码,这通常完全不是默认情况下应该做的事。
尽管这两个说法都是正确的,但如果需要,Swift 会给我们更多的控制权。
首先,是的,所有的 switch 语句必须是详尽的:你必须确保涵盖所有可能的值。如果你要对一个字符串进行切换,显然不可能详尽地检查所有可能的字符串,因为字符串的数量是无限的,所以这时我们需要提供一个默认情况 —— 当其他所有情况都不匹配时运行的代码。
例如,我们可以对一个包含地名的字符串进行切换:
let place = "Metropolis"
switch place {
case "Gotham":
print("你是蝙蝠侠!")
case "Mega-City One":
print("你是特警判官!")
case "Wakanda":
print("你是黑豹!")
default:
print("你是谁?")
}最后那个 default: 就是默认情况,当所有 case 都不匹配时,就会运行它。
记住:Swift 会按顺序检查各个 case,并运行第一个匹配的 case。 如果你把 default 放在任何其他 case 之前,那个 case 就没用了,因为它永远不会被匹配到,而且 Swift 会拒绝编译你的代码。
其次,如果你明确希望 Swift 继续执行后续的 case,可以使用 fallthrough。这不常用,但有时候 —— 只是有时候 —— 它能帮助你避免重复工作。
例如,有一首著名的圣诞歌曲叫《圣诞节的十二天》,随着歌曲的推进,越来越多的礼物堆在一个不幸的人身上,到第六天左右,这个人的房子已经相当满了。
我们可以用 fallthrough 来简单模拟这首歌。首先,这是不使用 fallthrough 时的代码样子:
let day = 5
print("我挚爱的人给了我……")
switch day {
case 5:
print("5 个金戒指")
case 4:
print("4 只鸣鸟")
case 3:
print("3 只法国母鸡")
case 2:
print("2 只斑鸠")
default:
print("梨树上的一只鹧鸪")
}这会打印 “5 个金戒指”,但这并不完全正确。第一天只应该打印 “梨树上的一只鹧鸪”,第二天应该打印 “2 只斑鸠”然后是 “梨树上的一只鹧鸪”,第三天应该打印 “3 只法国母鸡”、“2 只斑鸠”,…… 嗯,你明白了。
我们可以用 fallthrough 来获得完全那样的行为:
let day = 5
print("我挚爱的人给了我……")
switch day {
case 5:
print("5 个金戒指")
fallthrough
case 4:
print("4 只鸣鸟")
fallthrough
case 3:
print("3 只法国母鸡")
fallthrough
case 2:
print("2 只斑鸠")
fallthrough
default:
print("梨树上的一只鹧鸪")
}这会匹配第一个 case 并打印 “5 个金戒指”,但 fallthrough 这一行意味着会执行 case 4 并打印 “4 只鸣鸟”,而 case 4 又使用了 fallthrough,所以会打印 “3 只法国母鸡”,依此类推。这虽然不能完美匹配这首歌,但至少你可以看到这个功能的作用!
【可选阅读】什么时候应该使用 switch 语句而不是 if?
作者:Paul Hudson 2021 年 10 月 6 日 已针对 Xcode 16.4 更新
Swift 开发者可以同时使用 switch 和 if 来检查代码中的多个值,而且通常没有确凿的理由让你必须选择其中一个而不是另一个。话虽如此,有三个理由可能会让你考虑使用 switch 而不是 if:
- Swift 要求其
switch语句是详尽的,这意味着你必须要么为要检查的每个可能值都有一个case块(例如枚举的所有情况),要么必须有一个default情况。if和else if则没有这个要求,所以你可能会不小心漏掉某个情况。 - 当你使用
switch来检查一个值的多种可能结果时,这个值只会被读取一次,而如果你使用if,它会被读取多次。当你开始使用函数调用时,这一点就变得更为重要了,因为有些函数调用可能很慢。 - Swift 的
switch情况允许进行高级模式匹配,而用if来做就会很麻烦。
还有一种情况,但有点模糊:一般来说,如果你想检查同一个值的三种或更多可能状态,人们会更愿意使用 switch 而不是 if,至少从可读性来看是这样 —— 这样更清楚我们是在反复检查同一个值,而不是编写不同的条件。
附言:我已经介绍了 fallthrough 关键字,因为对于来自其他编程语言的人来说它很重要,但在 Swift 中很少见到它的使用 —— 如果你很难想到它可能有用的场景,也不用担心,因为说实话大多数时候它确实没什么用!
【练习题】switch 语句
问题 1/6:关于 switch case,以下哪些陈述是正确的?
- 选项 1: 你把要检查的值放在开头。
- 选项 2: switch 语句最适合用于常量。
问题 2/6:关于 switch case,以下哪些陈述是正确的?
- 选项 1: 你只能使用
fallthrough一次。 - 选项 2: 你必须列出所有结果作为
case语句,或者使用default。
问题 3/6:关于 switch case,以下哪些陈述是正确的?
- 选项 1: 如果你想匹配所有其他值,使用
default。 - 选项 2: switch 语句会使你的代码运行得更慢。
问题 4/6:关于 switch case,以下哪些陈述是正确的?
- 选项 1: 默认情况写成
default:而不是case default:。 - 选项 2: 最多可以有 10 个 case。
问题 5/6:关于 switch case,以下哪些陈述是正确的?
- 选项 1: 如果你使用
fallthrough,下一个 case 的代码将会运行。 - 选项 2: 总是需要默认情况。
问题 6/6:关于 switch case,以下哪些陈述是正确的?
- 选项 1: 与多个
if条件相比,switch 语句通常更易读。 - 选项 2: switch 语句不能用于整数。
5.4 如何使用三元条件运算符进行快速测试
作者:Paul Hudson 2021 年 10 月 4 日 已针对 Xcode 16.4 更新
在 Swift 中,还有最后一种检查条件的方法,当你看到它时,可能会想它在什么时候有用。公平地说,在很长一段时间里,我很少使用这种方法,但你稍后会发现,它在 SwiftUI 中确实非常重要。
这个选项被称为三元条件运算符。要理解它为什么叫这个名字,你首先需要知道+、-、==等都被称为二元运算符,因为它们处理两个输入:例如,2 + 5处理 2 和 5。
三元运算符处理三个输入,实际上,因为三元条件运算符是 Swift 中唯一的三元运算符,你经常会听到它被简称为 “三元运算符”。
好了,关于名称就说这么多:它实际上是做什么的呢?三元运算符让我们可以检查一个条件,并返回两个值中的一个:条件为真时返回一个值,条件为假时返回另一个值。
例如,我们可以创建一个名为age的常量来存储某人的年龄,然后创建第二个常量canVote来存储该人是否有投票权:
let age = 18
let canVote = age >= 18 ? "是" : "否"当这段代码运行时,canVote将被设置为 “是”,因为age被设置为 18。
如你所见,三元运算符分为三部分:一个检查(age >= 18),条件为真时的值(“是”),以及条件为假时的值(“否”)。这和常规的if和else块完全一样,顺序也相同。
如果有帮助的话,斯科特・米肖德提出了一个有用的记忆法:WTF。它代表 “what,true,false”(什么,真,假),与我们代码的顺序相匹配:
- 我们的条件是什么?是
age >= 18。 - 条件为真时做什么?返回 “是”,以便存储在
canVote中。 - 条件为假时呢?返回 “否”。
让我们看看其他一些例子,先从一个简单的例子开始,它读取 24 小时制的小时数并打印两条消息中的一条:
let hour = 23
print(hour < 12 ? "现在是中午之前" : "现在是中午之后")注意,这里没有将结果分配到任何地方 —— 根据hour的值,要么打印真的情况,要么打印假的情况。
再看一个例子,它将数组的count作为条件的一部分,然后返回两个字符串中的一个:
let names = ["Jayne", "Kaylee", "Mal"]
let crewCount = names.isEmpty ? "没人" : "\(names.count)人"
print(crewCount)当你的条件使用==来检查相等性时,读起来会有点困难,就像这里看到的:
enum Theme {
case light, dark
}
let theme = Theme.dark
let background = theme == .dark ? "黑色" : "白色"
print(background)通常人们觉得= theme ==这部分很难读,但记住要把它分解开:
- 什么?
theme == .dark - 真:“黑色”
- 假:“白色”
所以如果主题等于.dark,就返回 “黑色”,否则返回 “白色”,然后把它赋给background。
现在,你可能想知道为什么三元运算符有用,特别是当我们有常规的if/else条件可用时。我知道这不是一个很好的答案,但请相信我:在某些时候,特别是在 SwiftUI 中,我们别无选择,必须使用三元运算符。
你可以大致了解一下我们检查小时数的代码存在什么问题:
let hour = 23
print(hour < 12 ? "现在是中午之前" : "现在是中午之后")如果我们想用if和else来写,要么需要写这段无效的代码:
print(
if hour < 12 {
"现在是中午之前"
} else {
"现在是中午之后"
}
)要么运行两次print(),像这样:
if hour < 12 {
print("现在是中午之前")
} else {
print("现在是中午之后")
}第二个在这儿运行得很好,但在 SwiftUI 中就几乎不可能了,你稍后会看到。所以,即使你可能看着三元运算符,想知道为什么要用它,请相信我:它很重要!
【可选阅读】在 Swift 中什么时候应该使用三元运算符?
作者:Paul Hudson 2020 年 5 月 28 日 已针对 Xcode 16.4 更新
三元运算符让我们可以根据条件从两个结果中选择一个,而且方式非常简洁:
let isAuthenticated = true
print(isAuthenticated ? "欢迎!" : "你是谁?")有些人非常依赖三元运算符,因为它能写出非常简短的代码,而有些人则尽可能避免使用它,因为它会使代码更难读。
我非常支持 “尽可能避免” 的阵营,因为即使这段代码更长,我确实觉得它更容易理解:
if isAuthenticated {
print("欢迎")
} else {
print("你是谁?")
}现在,有一个时候三元运算符被大量使用,那就是在 SwiftUI 中。我不想在这里给出代码示例,因为可能会让人难以理解,但如果你愿意,在那里你真的可以大量使用三元运算符。即便如此,我还是喜欢在可能的情况下去掉它们,让我的代码更容易读,但你应该自己尝试,得出自己的结论。
【练习题】三元运算符
问题 1/12:这段代码会打印 “Success”—— 对还是错?
let phone = "iPhone"
print(phone == "Android" ? "Failure" : "Success")问题 2/12:这段代码会打印 “Success”—— 对还是错?
var retweets = "100"
print(retweets > 10 ? "Success" : "Failure")问题 3/12:这段代码会打印 “Success”—— 对还是错?
let numberOfCats = 21
print(numberOfCats >= 18 ? "Success" : "Failure")问题 4/12:这段代码会打印 “Success”—— 对还是错?
let dexterity = 18
print(dexterity >= 18 ? "Success" : "Failure")问题 5/12:这段代码会打印 “Success”—— 对还是错?
var averagePages: Double = 10.01
print(averagePages == 10 ? "Success" : "Failure")问题 6/12:这段代码会打印 “Success”—— 对还是错?
let storageSpace = 500
print(storageSpace > 1000 ? "Success" : "Failure")问题 7/12:这段代码会打印 “Success”—— 对还是错?
let isRecyclable = false
print(isRecyclable == true ? "Success" : "Failure")问题 8/12:这段代码会打印 “Success”—— 对还是错?
let isComplete: Bool = false
print(isComplete == true ? "Failure" : "Success")问题 9/12:这段代码会打印 “Success”—— 对还是错?
let strongMagnets = true
print(strongMagnets ? "Success" : "Failure")问题 10/12:这段代码会打印 “Success”—— 对还是错?
let highScore = 90
let actualScore = 89
print(actualScore > highScore ? "Success" : "Failure")问题 11/12:这段代码会打印 “Success”—— 对还是错?
let sides = 5
print(sides <= 6 ? "Success" : "Failure")问题 12/12:这段代码会打印 “Success”—— 对还是错?
var singers = ["Taylor Swift"]
print(singers == "Taylor Swift" ? "Success" : "Failure")你还记得这个系列课程的两条规则吗?你一直在坚持学习,这已经很好地践行了第一条规则(你太棒了!),但不要忘记第二条:把你的学习进度分享到网上,这样你就能得到更多的鼓励。