控制流
什么是控制流?
所谓控制流,是用来控制一件事物的流程的。跟数据类型一样,同样是模拟我们大脑对现实生活的处理模式。
还记得我在开始举的例子的吗?一起来回顾一下:
早晨起(床),吃完(早餐),出门穿上(外套),花(两元钱)买了公交(车票),到公司开始(干活)。
我们的大脑能够轻松处理不同的数据类型,并且,我们实际上还在处理流程控制。让我们将上面这个例子细分一下:
- 当我吃早饭时,只要没吃完,我就会一直吃,直到吃完或者吃饱;
- 出门的时候检查有没有穿外套,穿了外套再出门;
- 等公交车时,一辆接一辆,到了我要等的22路公交车,我就上车。
- 如果我赶上了公交车,我就会到公司干活;
看吧,这里隐藏了相当多的条件或循环。只有通过这种流程控制,我们才能自由处理各个对象。我们的大脑相当强大,强大到你根本不需要费劲思考,这些事情就顺理成章的完成了。
再说回可怜的计算机,它需要你给他交待具体的任务安排。就像家长给上小学的孩子辅导作业一样,你需要把自己的认知也暂时拉低到小学水平,这样你才能给孩子讲明白题目。
有哪些控制流?
明白了什么是控制流,我们再来看看,Swift能接受哪些具体的命令来组织事务的控制流程:
-
for-in
循环:多用在数据组合中,对单个数据进行逐项遍历; -
While
循环:如果某个条件满足,就一直循环,直到条件不满足; -
if
条件语句:如果某个条件满足,就做某件事,否则就做另一件事; -
Switch
匹配语句:列举很多条件,满足其中某个条件时,做某件事。
上面,只是简略的阐述了每个语句的意思,实际运用时,有很多细节需要注意,我们逐条来看一下。
1. for-in
循环
for-in
循环有点像是做上楼梯(每个台阶踩一遍),点名(每个人都要点一遍)这种比较机械的工作。
- 基本用法
for-in
循环我们之前已经多次用过了,在遍历数组,集合,字典中的值时,经常用到。
var arrayNumbers = [1,2,3]
for i in arrayNumbers {
print(i)
}
// 1,2,3
- 遍历区间
我们之前提到过区间,比如1...6
,就是1,2,3,4,5,6
这几个数字,也是可以用for in
进行遍历的。
for i in 1...6 {
print(i)
}
// 1,2,3,4,5,6
在这个例子中,i
是1-6范围的值,每一步都取了一次当前值,并打印出来。
我们还可以仅仅执行这段循环,而不打印出i
。
for i in 1...6 {
print("Hello")
}
// 打印了六遍"Hello"
在这个例子中,我们并没有在后面使用i
,因此,我们可以用_
代替i
。
for _ in 1...6 {
print("Hello")
}
// 同样打印了六遍"Hello"
- 步进遍历
上楼梯的时候,我可以两步两步上;也可以从二楼开始上(也许是坐电梯到的二楼?)。总之,有选择的可能。
这时,我们可以使用stride(from:to:by:)
或者是stride(from:through:by:)
,先看下面的例子:
for i in stride(from:2, to: 100, by: 5) {
print(i)
}
// 请自己在Playground中试一下结果。
这段代码实现了更加多的参数,在100
这个范围内,从2
开始数,每隔5
数一次。
2. While
循环
while condition {
statements
}
如果说for in
做的工作比较机械的话,while
,if
,switch
就比较有目的性了,我们先看while
。
- 基本用法
首先看一个示例,我会持续吃,直到我感觉有差不多八成饱。
这段描述可以用代码来表达:
var 饱腹感 = 1
while 饱腹感 < 8 {
print("再吃一口……")
饱腹感 += 1
}
分析这段代码:
首先,我很饿,用变量表示var 饱腹感 = 1
,我给自己设定的条件是要吃到八成饱,饱腹感 < 8
这实际上是一个判断,得出的就是一个判断值,也就是布尔值,是true or false
。
开始饱腹感是1,满足了饱腹感 < 8
这个判断,就会执行while
包起来的语句,我会再吃一口
,饱腹感
就会加1
。这时,代码运行完了,再从头运行,再次判断饱腹感
的值,直到饱腹感=8
的时候,条件就不满足了,就停止执行。
代码停止了,但是代码运行了7次再吃一口
,也达到了目的。
-
类比:
for-in
也能实现类似的多次执行的效果,但是while
更接近自然语言,可以类比中文当……时,就……
。在合适的情况下使用,代码的可读性就会更好。
这有点像上学的时候,老师给我们批改作文,会说你这里用词不当。怎么才能用词恰当呢?就需要多读,多写,慢慢进步。等到你成年后,你就不太会说:我已经饱的无与伦比了
,这样用词不当的话了。 -
repeat-while
repeat {
statements
} while condition
理解了while
的基本用法后,这里就很好理解。repeat-while
与while
的最大区别就是,无论什么条件,先执行一段代码。用我们的例子说明,就是管他饿不饿,先吃一口
。上面的代码可以改为:
var 饱腹感 = 8
repeat {
print("再吃一口……")
饱腹感 += 1
}
while 饱腹感 < 8
这里,我把饱腹感的初始值设为8,虽然达到我的八成饱要求了,可是还要首先吃一口再说。
3. if
条件语句
if
条件语句的意思是如果……就……
,如果某个条件满足,就执行某个动作。一个萝卜一个坑,对预期控制的比较精确,也是编程语言中最常用的逻辑控制语句。
- 基本用法
if condition {
statements
}
condition
同样是一个布尔值,statements
是某个动作。
举个与生活相关的例子,如果天气晴朗,我就出门
,用代码可以表示:
var 天气 = "晴朗"
if 天气 == "晴朗" {
print("我要出门")
}
声明一下,为了便于理解,我们使用了中文名称作为变量,swift也能正常执行,但是在正式开发中,尽量避免这样使用。我能想到的最大缺点就是,这样做会影响兼容性,因为其他大部分人都不这么干。
上面这段代码,if
的条件是成立的,因此会执行print()
语句,如果你把天气
的值改为其他,print()
就不会执行了。
- if else
既然是假设,那么我们还会有更复杂的情况,比如,如果天气晴朗,我就出门。如果阴天、下雨、大风等坏天气,我就在家上网。
这段描述,可以用代码表示如下:
var 天气 = "下雨"
if 天气 == "晴朗" {
print("我要出门")
} else {
print("在家上网")
}
这里,加了else
的意思是,除了天气 == "晴朗"
这个条件,其他任何情况下,执行的动作。
满足一个条件,执行一个命令。如果不满足这个条件,就执行另一个命令。
- if else if else
如果有多项判断,可以使用else if。在上面的例子中,我们可以细化对不同情况的反应。比如,如果天气晴朗,我就出门;如果下雨,我就带伞出门;否则,我就在家上网。
var 天气 = "下雨"
if 天气 == "晴朗" {
print("我要出门")
} else if 天气 == "下雨" {
print("带伞出门")
} else {
print("在家上网")
}
还有一种跟if
条件很类似的 guard
语句,主要在函数中使用,我们后面再说。
4. Switch
匹配语句
- 基本用法
如果有多项条件,每个条件都各自对应一种动作时,除了用多个else if
之外,还可以使用switch
语句。
var 天气 = "晴朗"
switch 天气 {
case "晴朗":
print("出门遛弯。")
case "下雨":
print("出门带伞。")
default:
print("在家上网。")
}
有没有发现switch
和if
的最大区别?if
后面的条件是布尔值,而switch
后面是具体的数据,case
后面的值也是从具体的数据中做选择。这样就灵活了很多。
但是要注意,case
是顺序执行,一旦满足,后面就不再执行了。
- 复合匹配
因此,我们可以轻松使用复合数据进行匹配
var 天气 = "阴天"
switch 天气 {
case "晴朗", "阴天":
print("出门遛弯。")
case "下雨":
print("出门带伞。")
default:
print("在家上网。")
}
我在第一行case
中同时列举了两个条件,阴天也能出门遛弯。如果是用if
,你需要这样列举天气=="晴朗" || 天气=="阴天"
,因为if
后面只能跟布尔值。
- 区间匹配
非布尔值带来的好处,还可以使用区间。举个另外的我们经常用的评分例子:
var 分数 = 86
switch 分数 {
case 0...60:
print("不及格")
case 61...80:
print("良好")
case 81...100:
print("优秀")
default:
print("分数有误")
}
- 元组匹配
switch还能按元组匹配,也很使用,看下面这个例子:
var 生命 = 90
var 魔法 = 86
switch (生命, 魔法) {
case (100, 100):
print("英雄的状态完美!")
case (_, 30...100):
print("可以释放魔法!")
case (0..<30, _):
print("生命值低,危险!")
default:
print("分数有误")
}
从这个例子,可以看到元组匹配的特性:能按元组精确匹配,使用_
可以忽略某项匹配,可以按区间匹配。
- 值绑定
我们可以在case
分支中,用常量或变量绑定条件中的值,这样,方便在该分支中,继续操作数据。
var status = (30,60)
switch status {
case let (生命, 魔法):
print("生命值:\(生命), 魔法值:\(魔法)")
let 状态值 = 生命 + 魔法
print("\(状态值)")
}
还可以配合where
, 进行条件判断。会让绑定值显得更有意义。
var status = (30,60)
switch status {
case let (生命, 魔法) where 生命 + 魔法 > 100:
print("可以释放魔法!")
case let (生命, 魔法):
print("生命值:\(生命), 魔法值:\(魔法)")
let 状态值 = 生命 + 魔法
print("状态值(\(状态值))不够,不能释放魔法。")
}
调整状态值,能得到不同case执行动作,你可以在Playground中多试试。
7. 控制转移语句
最后,我们来简单介绍几条控制语句.
continue
,break
,fallthrough
,return
,throw
return多用在函数中,throw多用在错误处理中,我们也放在后面介绍。
continue
,break
,fallthrough
这几个语句主要用在前面所说的各种循环语句中,因为有时候这些循环的默认执行状态并不能满足需求,这时候,可以搭配这些语句,来控制流程的走向。
下面只是举了部分例子,先想象结果如何,然后在Playground中验证试试,也可以按自己的想法修改、调试:
continue
var 天气 = ["晴朗","雷暴","阴天","下雨"]
for i in 天气 {
if i == "雷暴" {
continue
} else {
print("\(i)可以出门")
}
}
在遍历循环的时候,如果遇到“雷暴”,用continue跳过,不做任何事情。
break
var 天气 = ["晴朗","阴天","下雨","雷暴","台风"]
for i in 天气 {
if i == "雷暴" {
break
} else {
print("\(i)可以出门")
}
}
在遍历的过程中,遇到“雷暴”,整条循环就停止了,后面就不会继续判断了。
fallthrough
var 天气 = ["晴朗","阴天","下雨","雷暴","台风"]
for i in 天气 {
switch i {
case "晴朗":
print("可以出门")
case "阴天":
// print("可以出门")
fallthrough
case "下雨":
print("带伞出门")
default:
print("在家上网!")
}
}
在本段代码中,fallthrough
使本段代码直接交给下一条case
处理。使得“阴天”这条case也会打印“带伞出门。”
小结与预告
掌握了流程控制,我们就可以对数据进行操控了。编程的本质,无非是一系列的假设,判断,循环等等。
我们可以利用已经学到的知识,写很多东西了。比如,
- 根据圆的半径,求圆面积;
- 已知一个班10个同学的成绩,根据他们的分数进行不及格,良,优的评级;
- 已知一位英雄的生命值和魔法值,每释放一次魔法,就消耗10点生命和60点魔法,直到某一个值不能满足最低的安全值。
这是三个例子,证明你根据已有知识,是能够写出这样的程序的,哪怕简单一些。
这也是三个作业,没有标准答案,你试着独立写一写,对我们学习下一节很有帮助。
来点小提示:
- 算术运算符处理数字
- if或switch判断分数的范围
- 用while循环包住一个if判断,在其中进行变量的增减操作
网友评论