如果说莎士比亚的《哈姆雷特》中有一句话是大多数人都知道的,那就是“存在与否,这就是问题所在。”莎士比亚的意思是,这是生死攸关的终极问题,但恰好哈姆雷特击中了编程逻辑的核心:评估一个条件是否真实。
今天,我们将进入Swift的真正细节:运算符和条件,这让我们在程序运行时评估其状态,并根据结果采取不同的行动。有几种方法可以这样做,你需要它们全部,但我们会一步一步地进行,以便你可以看到它们是如何比较的。
如何检查条件是真还是假
程序经常做出选择:
如果学生的考试成绩超过80分,则打印成功信息。
如果用户输入的名字在朋友名字之后按字母顺序排列,请先输入朋友的名字。
如果将数字添加到数组中使其包含超过3个项目,请删除最早的项目。
如果用户被要求输入他们的姓名,但什么也没输入,请给他们一个默认名称“匿名”。
Swift用if
语句处理这些,这让我们检查一个条件,如果条件为真,则运行一些代码。它们看起来像这样:
if someCondition {
print("Do something")
}
让我们来分解一下:
条件以
if
开头,这向Swift发出信号,我们想检查代码中的某种条件。someCondition
部分是你写条件的地方——分数超过80分吗?数组是否包含超过3个项目?如果条件是真实的——如果分数真的超过80——那么我们打印“做点什么”的信息。
当然,这不是代码中的全部:我没有提到小{
和}
符号。这些被称为牙套——更具体地说,是打开和关闭牙套——尽管有时你会听到它们被称为“大发牙套”或“大发大括号”。
这些大括号在Swift中广泛用于标记代码块:开头大括号启动块,闭合大括号结束它。代码块内是我们想要运行的所有代码,如果我们的条件在检查时恰好为真,在我们的案例中,这是打印一条消息。
你可以在那里包含尽可能多的代码:
if someCondition {
print("Do something")
print("Do something else")
print("Do a third thing")
}
当然,真正重要的是someCondition
部分,因为这就是你的检查代码:你真正想检查什么条件?
好吧,让我们试试分数示例:如果score
常数超过80,让我们打印一条消息。以下是代码中的样子:
let score = 85
if score > 80 {
print("Great job!")
}
在该代码中,score > 80
是我们的条件。你会记得>
学校的意思是“大于”,所以我们的完整条件是“如果分数大于80”。如果它大于80,“干得好!”将被打印出来——很好!
那个>
符号是一个比较运算符,因为它比较了两件事并返回一个布尔结果:左边的东西比右边的东西大吗?您还可以使用<
表示小于,>=
表示“大于或等于”,<=
表示“小于或等于”。
让我们试试吧——你认为这个代码会打印什么?
let speed = 88
let percentage = 85
let age = 18
if speed >= 88 {
print("Where we're going we don't need roads.")
}
if percentage < 85 {
print("Sorry, you failed the test.")
}
if age >= 18 {
print("You're eligible to vote")
}
尝试在脑海中精神上运行代码——哪些print()
行将实际运行?
好吧,如果speed
大于或等于88,我们的第一个将运行,因为它正好是88,firstprintprint()
代码将运行。
如果percentage
小于85,第二个将运行,由于它正好是85,secondprintprint()
将不会运行——我们使用了小于、不小于或等于。
如果age
大于或等于18,第三个将运行,由于正好是18,第三个print()
将运行。
现在让我们试试我们的第二个示例条件:如果用户输入的名字按字母顺序排在朋友名字之后,请先输入朋友的名字。你已经看到了<
、>=
和其他数字如何很好地配合,但它们也同样适合开箱即用的字符串:
let ourName = "Dave Lister"
let friendName = "Arnold Rimmer"
if ourName < friendName {
print("It's \(ourName) vs \(friendName)")
}
if ourName > friendName {
print("It's \(friendName) vs \(ourName)")
}
因此,如果按字母顺序排序时,ourName
中的字符串在friendName
中的字符串之前,它会先打印ourName
然后打印friendName
,正如我们想要的那样。
让我们来看看我们的第三个示例条件:如果将一个数字添加到数组中使其包含超过3个项目,请删除最古老的一个。您已经满足了append()count
和remove(at:)
因此我们现在可以用如下条件将这三项放在一起:
// Make an array of 3 numbers
var numbers = [1, 2, 3]
// Add a 4th
numbers.append(4)
// If we have over 3 items
if numbers.count > 3 {
// Remove the oldest number
numbers.remove(at: 0)
}
// Display the result
print(numbers)
现在让我们来看看我们的第四个示例条件:如果用户被要求输入他们的名字,但根本没有输入任何东西,请给他们一个默认名称“匿名”。
要解决这个问题,您首先需要满足另外两个您将经常使用的比较运算符,这两个运算符都处理相等。第一个是==
,意思是“等于”,如下所用:
let country = "Canada"
if country == "Australia" {
print("G'day!")
}
第二个是!=
,意思是“不等于”,像这样使用:
let name = "Taylor Swift"
if name != "Anonymous" {
print("Welcome, \(name)")
}
在我们的案例中,我们想检查用户输入的用户名是否为空,我们可以这样做:
// Create the username variable
var username = "taylorswift13"
// If `username` contains an empty string
if username == "" {
// Make it equal to "Anonymous"
username = "Anonymous"
}
// Now print a welcome message
print("Welcome, \(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"
}
如何检查多种条件
当我们使用if
,我们必须为Swift提供某种条件,一旦它被评估,要么是真,要么是假。如果您想检查几个不同的值,您可以像这样一个接一个地放置它们:
let age = 16
if age >= 18 {
print("You can vote in the next election.")
}
if age < 18 {
print("Sorry, you're too young to vote.")
}
然而,如果你想一想,这并不太有效:我们的两个条件是相互排斥的,因为如果有人大于或等于18(第一个条件),那么他们不能小于18(第二个条件),反之亦然。我们正在让Swift做不需要的工作。
在这种情况下,Swift为我们提供了一个更高级的条件,允许我们在代码中添加一个else
块——如果条件不为真,可以运行一些代码。
使用else
,我们可以将之前的代码重写为:
let age = 16
if age >= 18 {
print("You can vote in the next election.")
} else {
print("Sorry, you're too young to vote.")
}
现在Swift只需要检查一次age
:如果它大于或等于18,则运行第一个print()
代码,但如果任何值小于18,则运行第二个print()
代码。
所以,现在我们的状况是这样的:
if someCondition {
print("This will run if the condition is true")
} else {
print("This will run if the condition is false")
}
There’s an even more advanced condition called else if
, which lets you run a new check if the first one fails. You can have just one of these if you want, or have multiple else if
, and even combine else if
with an else
if needed. However, you can only ever have one else
, because that means “if all the other conditions have been false.”
看起来是这样的:
let a = false
let b = true
if a {
print("Code to run if a is true")
} else if b {
print("Code to run if a is false but b is true")
} else {
print("Code to run if both a and b are false")
}
如果你愿意,你可以继续添加越来越多的else if
条件,但要注意你的代码不要太复杂!
除了使用else
和else if
来制作更高级的条件外,您还可以检查不止一件事。例如,我们可能想说“如果今天的温度超过20摄氏度,但低于30摄氏度,请打印一条信息。”
这有两个条件,所以我们可以这样写:
let temp = 25
if temp > 20 {
if temp < 30 {
print("It's a nice day.")
}
}
虽然这足够有效,但Swift提供了一个更短的替代方案:我们可以使用&&
将两个条件组合在一起,只有当条件中的两个部分为真时,整个条件才会为真。
所以,我们可以将我们的代码更改为:
if temp > 20 && temp < 30 {
print("It's a nice day.")
}
你应该把&&
读成“和”,所以我们的整个条件是“如果温度大于20,温度小于30,请打印一条消息。”它被称为逻辑运算符,因为它结合了布尔值来制作一个新的布尔值。
&&
有一个对应物是两个管道符号,||
,意思是“或”。只有当两个子条件都为真时&&
才会使条件为真,而||
如果任何一个子条件都为真,则将使条件为真。
例如,我们可以说,如果用户至少年满18岁,或者如果他们未满18岁,他们必须获得父母的许可,就可以购买游戏。我们可以用||
这样写:
let userAge = 14
let hasParentalConsent = true
if userAge >= 18 || hasParentalConsent == true {
print("You can buy the game")
}
这将打印“您可以购买游戏”,因为尽管我们的条件的前半部分失败——用户未满18岁——但后半部分通过,因为他们确实有父母的同意。
请记住,在条件中使用== true
可以删除,因为我们显然已经在检查布尔值。所以,我们可以改写这个:
if userAge >= 18 || hasParentalConsent {
print("You can buy the game")
}
为了完成检查多个条件,让我们尝试一个更复杂的示例,该示例同时结合了if
、else if
、else
和||
,甚至展示枚举如何适应条件。
在这个例子中,我们将创建一个名为TransportOption
的枚举,其中包含五个情况:飞机、直升机、自行车、汽车和滑板车。然后,我们将为常量分配一个示例值,并运行一些检查:
如果我们乘飞机或直升机去某个地方,我们会打印“让我们飞吧!”
如果我们骑自行车去,我们会打印“我希望有一条自行车道......”
如果我们开车去,我们会打印“是时候堵车了。”
否则,我们将打印“我现在要租一辆滑板车!”
这是那个代码:
enum TransportOption {
case airplane, helicopter, bicycle, car, scooter
}
let transport = TransportOption.airplane
if transport == .airplane || transport == .helicopter {
print("Let's fly!")
} else if transport == .bicycle {
print("I hope there's a bike path…")
} else if transport == .car {
print("Time to get stuck in traffic.")
} else {
print("I'm going to hire a scooter now!")
}
我想挑出那个代码的几个部分:
当我们设置
transport
值时,我们需要明确说明我们指的是TransportOption.airplane
。我们不能只写.airplane
因为Swift不明白我们指的是TransportOption
枚记。一旦发生这种情况,我们就不再需要编写
TransportOption
了,因为Swift知道transport
必须是某种TransportOption
。因此,我们可以检查它是否等于.airplane
而不是TransportOption.airplane
。使用
||
来检查transport
是等于.airplane
还是等于.helicopter
,如果其中任何一个都为真,则条件为真,并且“我们飞吧!”被打印出来。如果第一个条件失败——如果运输模式不是
.airplane
或.helicopter
——那么第二个条件是运行:运输模式是.bicycle
吗?如果是这样,“我希望有一条自行车道......”就打印出来了。如果我们也不骑自行车去,那么我们检查一下我们是否开车去。如果我们是,“是时候堵车了。”就打印出来了。
最后,如果之前的所有条件都失败了,那么
else
块就会运行,这意味着我们乘坐滑板车去。
如何使用switch语句来检查多个条件
您可以重复使用if
和else if
来检查条件,但它有点难以阅读。例如,如果我们有来自枚举的天气预报,我们可以根据一系列条件选择打印的消息,如下所示:
enum Weather {
case sun, rain, wind, snow, unknown
}
let forecast = Weather.sun
if forecast == .sun {
print("It should be a nice day.")
} else if forecast == .rain {
print("Pack an umbrella.")
} else if forecast == .wind {
print("Wear something warm")
} else if forecast == .rain {
print("School is cancelled.")
} else {
print("Our forecast generator is broken!")
}
那有用,但有问题:
我们不得不一直写
forecast
,尽管我们每次都在检查同样的东西。我不小心检查了两次
.rain
,尽管第二次检查永远不会是真的,因为只有当第一次检查失败时才会执行第二次检查。我根本没有检查
.snow
,所以我们缺少功能。
我们可以使用一种叫做switch
的不同检查条件的方法来解决这三个问题。这也让我们可以逐一检查单个案例,但现在Swift能够提供帮助。就枚数而言,它知道枚枚枚数可能存在的所有可能的情况,因此如果我们错过一个或检查两次,它就会抱怨。
因此,我们可以用这个替换所有if
和else if
检查:
switch forecast {
case .sun:
print("It should be a nice day.")
case .rain:
print("Pack an umbrella.")
case .wind:
print("Wear something warm")
case .snow:
print("School is cancelled.")
case .unknown:
print("Our forecast generator is broken!")
}
让我们来分解一下:
我们从
switch forecast
开始,它告诉Swift,这是我们想要检查的值。We then have a string of
case
statements, each of which are values we want to compare againstforecast
.我们每个案例都列出了一种天气类型,由于我们正在切换
forecast
,我们不需要写Weather.sun
、Weather.rain
等——Swift知道这一定是某种Weather
。在每个案例之后,我们编写一个冒号来标记要运行的代码的开始,如果该案例匹配。
我们使用闭合大括号来结束
switch
语句。
如果你尝试将.snow
更改为.rain
,你会看到Swift大声抱怨:一旦我们检查了两次.rain
,我们的switch
语句并不详尽无遗——它不能处理所有可能的情况。
如果您曾经使用过其他编程语言,您可能已经注意到Swift的switch
语句在两个地方有所不同:
所有
switch
语句都必须是详尽的,这意味着所有可能的值都必须在那里处理,这样您就不能意外地遗忘一个。Swift将执行第一个与您正在检查的条件匹配的案例,但不会执行更多。其他语言经常从所有后续情况下继续执行其他代码,这通常是完全错误的默认操作。
虽然这两个陈述都是正确的,但如果我们需要的话,Swift会给我们更多的控制。
首先,是的,所有switch
语句都必须是详尽的:您必须确保涵盖所有可能的值。如果您正在打开字符串,那么显然不可能对所有可能的字符串进行详尽检查,因为存在无限数量,因此我们需要提供一个默认情况——如果其他情况都不匹配,则需要运行代码。
例如,我们可以切换一个包含地名的字符串:
let place = "Metropolis"
switch place {
case "Gotham":
print("You're Batman!")
case "Mega-City One":
print("You're Judge Dredd!")
case "Wakanda":
print("You're Black Panther!")
default:
print("Who are you?")
}
default:
最后是默认案例,如果所有案例都不匹配,它将运行。
记住:Swift按顺序检查其案例,并运行第一个匹配的案例。如果您将default
放在任何其他案例之前,该案例是无用的,因为它永远不会匹配,Swift将拒绝构建您的代码。
其次,如果您明确希望Swift继续执行后续案例,请使用fallthrough
。这并不常用,但有时——只是有时——它可以帮助你避免重复工作。
例如,有一首著名的圣诞歌曲叫《圣诞节的十二天》,随着歌曲的继续,越来越多的礼物被堆在一个不幸的人身上,大约在第六天,房子已经满座了。
我们可以用fallthrough
对这首歌做一个简单的近似值。首先,以下是代码在没有fallthrough
的外观:
let day = 5
print("My true love gave to me…")
switch day {
case 5:
print("5 golden rings")
case 4:
print("4 calling birds")
case 3:
print("3 French hens")
case 2:
print("2 turtle doves")
default:
print("A partridge in a pear tree")
}
这将打印“5个金戒指”,这不太对。第1天只应打印“梨树上的鹧鸪”,第2天应该是“2只海龟鸽”,然后是“梨树上的鸷�”,第3天应该打印“3只法国母鸡”、“2只海龟鸽”,然后......好吧,你明白了。
我们可以使用fallthrough
来准确地获得该行为:
let day = 5
print("My true love gave to me…")
switch day {
case 5:
print("5 golden rings")
fallthrough
case 4:
print("4 calling birds")
fallthrough
case 3:
print("3 French hens")
fallthrough
case 2:
print("2 turtle doves")
fallthrough
default:
print("A partridge in a pear tree")
}
如何使用三目运算符进行快速测试
还有最后一个方法可以检查Swift中的条件,当你看到它时,你可能会想知道它什么时候有用。公平地说,在很长一段时间以来,我很少使用这种方法,但正如你稍后会看到的那样,它在SwiftUI中真的很重要。
这个选项被称为三元条件运算符。要理解它为什么有这个名字,你首先需要知道+
、-
、==
等都被称为二进制运算符,因为它们与两个输入一起使用2 + 5
,例如,与2和5一起使用。
三元运算符使用三个输入,事实上,由于三元条件运算符是Swift中唯一的三元运算符,你经常会听到它被称为“三元运算符”。
总之,名字就够了:这到底有什么作用?好吧,三元运算符允许我们检查一个条件,并返回两个值之一:如果条件为真,则为假,则为假。
例如,我们可以创建一个名为age
的常量来存储某人的年龄,然后创建一个名为canVote
的第二个常量,该常量将存储这个人是否能够投票:
let age = 18
let canVote = age >= 18 ? "Yes" : "No"
当该代码运行时,canVote
将设置为“是”,因为age
设置为18岁。
如您所见,三元运算符分为三部分:检查(age >= 18
),条件为真时(“是”),条件为假时的检查(“否”)。这使得它完全像一个常规的if
和else
块,顺序相同。
如果有帮助的话,Scott Michaud建议一个有用的助语:WTF。它代表“什么、真、假”,并符合我们代码的顺序:
我们的状况如何?嗯,
age >= 18
当条件属实时该怎么办?发送回“是”,以便将其存储在
canVote
。如果条件是错误的呢?发送回“否”。
让我们看看其他一些例子,从一个简单的例子开始,以24小时格式读取一小时,并打印两条消息中的一条:
let hour = 23
print(hour < 12 ? "It's before noon" : "It's after noon")
请注意,这并没有将结果分配到任何地方——根据hour
值,真或假情况都会被打印出来。
或者,这是一个将数组的count
作为其条件的一部分读取,然后将两个字符串中的一个发送回去:
let names = ["Jayne", "Kaylee", "Mal"]
let crewCount = names.isEmpty ? "No one" : "\(names.count) people"
print(crewCount)
当您的条件使用==
来检查相等时,它变得有点难以阅读,如您在这里看到:
enum Theme {
case light, dark
}
let theme = Theme.dark
let background = theme == .dark ? "black" : "white"
print(background)
= theme ==
部分通常是人们觉得很难阅读的部分,但记得把它分解:
什么?
theme == .dark
真实:“黑色”
错误:“白色”
因此,如果主题等于.dark
返回“黑色”,否则返回“白色”,然后将其分配给background
。
现在,你可能想知道为什么三元算子是有用的,特别是当我们有正则/else
条件可用时。我知道这不是一个好答案,但你必须相信我:有些时候,特别是在SwiftUI中,我们别无选择,必须使用三元。
您可以大致查看我们代码的问题所在,以检查时间:
let hour = 23
print(hour < 12 ? "It's before noon" : "It's after noon")
如果我们想用if
和else
写出来,我们需要写这个无效代码:
print(
if hour < 12 {
"It's before noon"
} else {
"It's after noon"
}
)
或者运行print()
两次,像这样:
if hour < 12 {
print("It's before noon")
} else {
print("It's after noon")
}
[本文翻译自hackingwithswift,点击链接阅读原文]
评论区