[翻译]Swift编程语言——控制流

控制流

for、while、if、switch和C基本一样
for-in扩展 for
Swift的switch比C语言的要强大。swift的case不再默认执行下一case了,这样会避免遗漏break,导致下一case分支被执行;case可以匹配多种类型,可以是一个范围、元组、或者特殊类型。匹配值可以绑定到一个常量或者变量中,这样case可以使用;复杂的匹配条件可以使用where。

for循环

for-in遍历一个范围、序列、集合中的每一个元素;for按照一个条件执行遍历,典型的就是在每次操作后计数加1

for-in

for-in循环用来便利集合中的每一项。比如一定范围内的数字、数组中的每一项、字符串中的每一个字符等。

​for​ ​index​ ​in​ ​1​...​5​ {
​ ​println​(​"​\(​index​)​ times 5 is ​\(​index​ * ​5​)​"​)
​}
​// 1 times 5 is 5
​// 2 times 5 is 10
​// 3 times 5 is 15
​// 4 times 5 is 20
​// 5 times 5 is 25

上面例子中遍历的范围是1-5的闭合区间(使用了范围操作符…),index会被赋予初始值1(也就是范围的第一个数字),循环体内可以被引用执行。这个例子中,只有一条语句,就是对当前index的值乘5,输出结果字符串。当语句被执行后,index的值被范围内的第二个数字2替换,循环体内的语句被再次执行。就这样,一直到范围的最后一项。例子中的index是一个常量,它会在每次循环前被设置新值。index是被非显示声明的常量,在循环语句中简话了let。
如果你在循环体内不使用范围内的每一项值,那么可以使用下划线替代常量(变量)名:

​let​ ​base​ = ​3
​let​ ​power​ = ​10
​var​ ​answer​ = ​1
​for​ ​_​ ​in​ ​1​...​power​ {
​ ​answer​ *= ​base
​}
​println​(​"​\(​base​)​ to the power of ​\(​power​)​ is ​\(​answer​)​"​)
​// prints "3 to the power of 10 is 59049"

上面例子是计算3的10次方。就是3连续乘自身10次(1-10的闭合区间)。循环体中不需要知道当前循环进行到的位置,这种情况下用下划线占位表示循环体内不需要引用当前循环值。
for-in遍历数组:

​let​ ​names​ = [​"Anna"​,​"Alex"​,​"Brian"​,​"Jack"​]
​for​ ​name​ ​in​ ​names​ {
​ ​println​(​"Hello,​\(​name​)​!"​)
​}
​// Hello,Anna!
​// Hello,Alex!
​// Hello,Brian!
​// Hello,Jack!

你也可以通过键值对遍历字典。当字典在遍历时,会返回每一项对应的一个元组(形式为:(key,value)),可以在循环体内使用常量接收元组内容。下面的例子中,字典的键对应的常量名字叫做animalName,字典的值对应的常量名字叫做legCount。

​let​ ​numberOfLegs​ = [​"spider"​: ​8​,​"ant"​: ​6​,​"cat"​: ​4​]
​for​ (​animalName​,​legCount​) ​in​ ​numberOfLegs​ {
​ ​println​(​"​\(​animalName​)​s have ​\(​legCount​)​ legs"​)
​}
​// ants have 6 legs
​// cats have 4 legs
​// spiders have 8 legs

字典中的内容在遍历的时候可能和插入的顺序不同,因为字典天生就不是顺序存放内容的容器,不能保证插入顺序和遍历时的顺序一致。
for-int 同样可以使用在遍历字符串中字符的情形下:

for​ ​character​ ​in​ ​"Hello"​ {
​ ​println​(​character​)
​}
​// H
​// e
​// l
​// l
​// o

for

一般的形式:

for initialization; condition; increment {

    statements

}

和C语言不同的就是缺少了圆括号。
执行顺序如下:
1:当遍历开始,初始化语句执行一次,做的工作是设置在循环体内使用的常量、变量。
2:条件语句被计算。如果条件判断为假,循环结束,执行末尾花括号之后的语句。如果条件判断为真,执行循环体内的语句。
3:循环体内语句执行完后,increment 语句被执行。一般会做一个计数器的自增或者自减或者修改一个初始化的值。这个语句被执行后,跳转到2,条件语句被再次执行。
上述的内容其实是下面语句的简化版本:

initialization
while condition {
    statements
    increment
}

上面的格式中,常量和变量的定义在初始化语句中进行,变量和常量的作用范围只限于循环内部。如果你在循环结束后也使用定义的内容,那么你就需要在进入循环前就定义:

var​ ​index​: ​Int
​for​ ​index​ = ​0​; ​index​ < ​3​; ++​index​ {
​ ​println​(​"index is ​\(​index​)​"​)
​}
​// index is 0
​// index is 1
​// index is 2
​println​(​"The loop statements were executed ​\(​index​)​ times"​)
​// prints "The loop statements were executed 3 times"

这里index的值是3,不是2。因为index是2执行完后会执行++index,index已经变成了3,index<3的条件不成立,退出循环。

while循环

while循环,一直执行循环体的内容知道条件判断为假。尤其适合事先不知道迭代次数的情况。Swift提供两个格式的while:
1:while在每次执行循环体前判断条件。
2:do-while在每次执行循环体后判断条件

while

while condition {

    statements

}

下面的游戏名字叫做《蛇和梯子》:

游戏规则是这样的:
1:有25个格子,目标是到达25格,或超过25格。
2:每回合,你掷一个6面的色子,根据色子上的个数,决定走几步。行进的方向按照虚线指引。
3:如果回合结束你到了一个梯子的底部,那么爬上梯子到达另外一端。
4:如果回合结束你到了一条蛇的头部,你将退回到那条蛇的尾部(If your turn ends at the head of a snake,you move down that snake.)
游戏面板用一个Int数组表示,它的长度按根据一个叫做finalSquare的常量而定。这个常量也是判断游戏胜利的条件。游戏面板被初始化为26个0,而不是25个,索引从0开始到25结束。:
let​ ​finalSquare​ = ​25
​var​ ​board​ = [​Int​](​count​: ​finalSquare​ + ​1​,​repeatedValue​: ​0​)
需要给蛇和梯子添加额外的数据。位于梯子底部的格子赋值给一个整数来实现爬上;反之蛇头到达一个被赋值为一个负数的格子,就会爬下去。
board​[​03​] = +​08​; ​board​[​06​] = +​11​; ​board​[​09​] = +​09​; ​board​[​10​] = +​02
​board​[​14​] = -​10​; ​board​[​19​] = -​11​; ​board​[​22​] = -​02​; ​board​[​24​] = -​08
3号格子是梯子的底部,你可以顺着梯子爬到11.为了表现这个,borad[03]被赋值为+8。+08是出于书写整洁考虑,+为了和-对应,10以下的数字前面添加0也是为了整齐。

玩家的起始位置在0号格子,就是游戏面板的左下角。下面给出第一种掷色子的方式:

var​ ​square​ = ​0
​var​ ​diceRoll​ = ​0
​while​ ​square​ < ​finalSquare​ {
​ ​// roll the dice
​ ​if​ ++​diceRoll​ == ​7​ { ​diceRoll​ = ​1​ }
​ ​// move by the rolled amount
​ ​square​ += ​diceRoll
​ ​if​ ​square​ < ​board​.​count​ {
​ ​// if we're still on the board,move up or down for a snake or a ladder
​ ​square​ += ​board​[​square​]
​ }
​}
​println​(​"Game over!"​)

这是一个非常简单的掷色子方式。没有采用随机算法得到色子的点数,而是给出初始值0。每次循环都做一次自加操作,并作值检查,防止过大。当值过大(到7)的时候,被置为1。所以这个方式给出的色子点数序列一直都是这样的:“1,2,3,4,5,6,1,2……”。
循环体内执行完掷色子的操作后,玩家开始按照色子的点数移动。到达25号格子,游戏就结束了。为了达到这个目标,代码先做了检查:square 是不是还小于格子的总数。如果不是,游戏结束;如果是,执行如下语句:将board[square]的值加到square上(决定了是向上爬还是向下爬)。
如果没有做这个检查,board[square]会数组越界,造成运行时错误。
执行到循环体的最后,循环条件会被再次检查,确定是否可以再次执行循环。如果到达或超过25号格子,循环条件检查为假,游戏结束。
while适用这样的场合:循环开始前循环次数不定,循环会一直执行知道循环条件不成立。

do-while

do-while是另外一种while循环。在判断循环条件前,先执行循环体内容,直到循环条件判断为假。
基本形式:

do {

    statements

} while condition

下面还是以蛇和梯子为例,用do-while实现。finalSquare,board,square,和 diceRoll在循环前就被初始化好了:

​let​ ​finalSquare​ = ​25
​var​ ​board​ = [​Int​](​count​: ​finalSquare​ + ​1​,​repeatedValue​: ​0​)
​board​[​03​] = +​08​; ​board​[​06​] = +​11​; ​board​[​09​] = +​09​; ​board​[​10​] = +​02
​board​[​14​] = -​10​; ​board​[​19​] = -​11​; ​board​[​22​] = -​02​; ​board​[​24​] = -​08
​var​ ​square​ = ​0
​var​ ​diceRoll​ = ​0

这个版本的游戏中,循环中第一步要做的就是判断是梯子还是蛇。没有梯子直通25号格子,所以不可能通过梯子赢得游戏,进而检查时梯子还是蛇是安全的。
在游戏的开始,玩家处于0号格子:

​do​ {
​ ​// move up or down for a snake or ladder
​ ​square​ += ​board​[​square​]
​ ​// roll the dice
​ ​if​ ++​diceRoll​ == ​7​ { ​diceRoll​ = ​1​ }
​ ​// move by the rolled amount
​ ​square​ += ​diceRoll
​} ​while​ ​square​ < ​finalSquare
​println​(​"Game over!"​)

在检查完是梯子还是蛇的语句执行完,开始摇色子,玩家按照摇出的点数走格子。循环体继续执行,直到循环结束。
条件检查语句和前一个例子一样,区别就是这个语句要在循环体执行完后才会被执行。在这个do-while循环中,square += board[square]会在循环条件判断完成后立即执行。它将条件检查后移了。

条件语句

两种条件语句:if和switch,前者使用简单的情况;后者适用多分枝的情况。

if

当它之后的条件语句为真时,才会执行一系列语句。

​var​ ​temperatureInFahrenheit​ = ​30
​if​ ​temperatureInFahrenheit​ <= ​32​ {
​ ​println​(​"It's very cold. Consider wearing a scarf."​)
​}
​// prints "It's very cold. Consider wearing a scarf."

这个例子检查温度师傅低于华氏32度(水的冰点)。如果为真,打印;否则不做任何事。继续执行if括号后的内容。
if语句提供了一个选项,就是else。当条件为假的时候,执行else部分的内容:

temperatureInFahrenheit​ = ​40
​if​ ​temperatureInFahrenheit​ <= ​32​ {
​ ​println​(​"It's very cold. Consider wearing a scarf."​)
​} ​else​ {
​ ​println​(​"It's not that cold. Wear a t-shirt."​)
​}
​// prints "It's not that cold. Wear a t-shirt."

两个分支中的一个会被执行。因为温度是华氏40度,还没有冷到戴围巾的程度,所以进入else分支。

你可以将多个if语句一起使用,以使用条件的判断:

temperatureInFahrenheit​ = ​90
​if​ ​temperatureInFahrenheit​ <= ​32​ {
​ ​println​(​"It's very cold. Consider wearing a scarf."​)
​} ​else​ ​if​ ​temperatureInFahrenheit​ >= ​86​ {
​ ​println​(​"It's really warm. Don't forget to wear sunscreen."​)
​} ​else​ {
​ ​println​(​"It's not that cold. Wear a t-shirt."​)
​}
​// prints "It's really warm. Don't forget to wear sunscreen."

这里,一个if语句被添加以应对特定热的温度情况。结束的else被保留,打印的内容是不是特别冷夜不是特别热的情况。

最后的一个else是可选的,根据需要决定是否使用:

temperatureInFahrenheit​ = ​72
​if​ ​temperatureInFahrenheit​ <= ​32​ {
​ ​println​(​"It's very cold. Consider wearing a scarf."​)
​} ​else​ ​if​ ​temperatureInFahrenheit​ >= ​86​ {
​ ​println​(​"It's really warm. Don't forget to wear sunscreen."​)
​}

在这个例子中,温度不冷不热的情况下,没有任何信息输出。

switch

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提供了多种方式匹配内容。下面将会一一讲到。
每个switch的case内容都是一个代码分支,这和if语句相似。Switch决定了哪个分支会被执行。
每个switch语句必须是彻底细分的。就是每个可能值的类型都要有对应的case匹配,如果没有case匹配的情况,需要定义一个默认匹配所有情况的分支。这样做需要使用关键字:default,一般它会出现在最后。
下面的例子评估一个小写字符(someCharacter):

let​ ​someCharacter​: ​Character​ = ​"e"
​switch​ ​someCharacter​ {
​case​ ​"a"​,​"e"​,​"i"​,​"o"​,​"u"​:
​ ​println​(​"​\(​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"​:
​ ​println​(​"​\(​someCharacter​)​ is a consonant"​)
​default​:
​ ​println​(​"​\(​someCharacter​)​ is not a vowel or a consonant"​)
​}
​// prints "e is a vowel"

例子中第一个case匹配了所有英文的元音字母;第二个case匹配了所有的英文辅音字母。
你不可能将所有可能的情况作为一个case,所以提供了default来匹配所有其他情况。这样确保了switch是彻底细分的。

没有默认的接续执行(No Implicit Fallthrough)

和C语言和OC不同,Swift的switch不在默认依次执行每个case,而是只执行最初的匹配到的case,这样省得要写break了。这让switch变得更安全和简单,避免了多于一个的case会被执行。
尽管不用在case的执行结束写break了,但是break还是有用的,用于忽略特定的case或者在case没有完全执行完之前跳出。具体的描述后文有述。

每个case中至少要包含一条语句,否则不能通过编译:

let​ ​anotherCharacter​: ​Character​ = ​"a"
​switch​ ​anotherCharacter​ {
​case​ ​"a"​:
​case​ ​"A"​:
​ ​println​(​"The letter A"​)
​default​:
​ ​println​(​"Not the letter A"​)
​}

​// this will report a compile-time error
上例中和C语言的不同之处在于,不能同时匹配“a”和“A”,反而会出现编译错误:case a没有包含任何可执行的语句。这样的设定会避免默认顺序执行,安全而且意图更加明确。

多个内容可以匹配同一个case,只要用逗号分隔他们,像下面这样:

switch some value to consider {

case value 1,value 2:

    statements

}

如果想要case顺序执行,可以使用关键字“fallthrough”,后文有述。

范围匹配

switch的case可以按照一个范文匹配值。下面的例子将一个数字转换到一个范围内:

let​ ​count​ = ​3_000_000_000_000
​let​ ​countedThings​ = ​"stars in the Milky Way"
​var​ ​naturalCount​: ​String
​switch​ ​count​ {
​case​ ​0​:
​ ​naturalCount​ = ​"no"
​case​ ​1​...​3​:
​ ​naturalCount​ = ​"a few"
​case​ ​4​...​9​:
​ ​naturalCount​ = ​"several"
​case​ ​10​...​99​:
​ ​naturalCount​ = ​"tens of"
​case​ ​100​...​999​:
​ ​naturalCount​ = ​"hundreds of"
​case​ ​1000​...​999_999​:
​ ​naturalCount​ = ​"thousands of"
​default​:
​ ​naturalCount​ = ​"millions and millions of"
​}
​println​(​"There are ​\(​naturalCount​)​ ​\(​countedThings​)​."​)
​// prints "There are millions and millions of stars in the Milky Way."

元组

你可以使用元组在一个case中尝试匹配多个值。每个元组的元素可以和一个值或者一个范围做比较。或者你可以使用”_”做通配符匹配任意值。
下面的例子使用一个元组表示一个点,然后对这个点进行归类:

let​ ​somePoint​ = (​1​,​1​)
​switch​ ​somePoint​ {
​case​ (​0​,​0​):
​ ​println​(​"(0,0) is at the origin"​)
​case​ (​_​,​0​):
​ ​println​(​"(​\(​somePoint​.​0​)​,0) is on the x-axis"​)
​case​ (​0​,​_​):
​ ​println​(​"(0,​\(​somePoint​.​1​)​) is on the y-axis"​)
​case​ (-​2​...​2​,-​2​...​2​):
​ ​println​(​"(​\(​somePoint​.​0​)​,​\(​somePoint​.​1​)​) is inside the box"​)
​default​:
​ ​println​(​"(​\(​somePoint​.​0​)​,​\(​somePoint​.​1​)​) is outside of the box"​)
​}
​// prints "(1,1) is inside the box"

例子中,switch的cse对应几种情况:原点、x轴、y轴、在以远点为中心的4*4正方形中、在正方形之外。
和C语言不同的是,Swift允许Switch的不同case中存在相同的一个或多个值。实际上,原点可以匹配所有的4个case,如果有这样多个case都满足的情形,只有第一个case生效。(0,0)匹配到了原点后,其他的case被忽略了。

值绑定

switch的case可以绑定值给临时的常量或者变量,这些临时敞亮或者变量可以在case块内使用。这被称作value binding,因为值给绑定到了常量或者变量之上,在case中使用。
下面的例子用一个元组表示一个点,然后在图表中对其进行归类:

let​ ​anotherPoint​ = (​2​,​0​)
​switch​ ​anotherPoint​ {
​case​ (​let​ ​x​,​0​):
​ ​println​(​"on the x-axis with an x value of ​\(​x​)​"​)
​case​ (​0​,​let​ ​y​):
​ ​println​(​"on the y-axis with a y value of ​\(​y​)​"​)
​case​ ​let​ (​x​,​y​):
​ ​println​(​"somewhere else at (​\(​x​)​,​\(​y​)​)"​)
​}
​// prints "on the x-axis with an x value of 2"

例子中switch判断点是在x轴、y轴还是不在任何轴上。
三个case中定义了对应x、y的常量占位符,来匹配anotherPoint元组中的值。第一个case中, case (let x,0)匹配所有y值是0的点并且将该点的横坐标赋值给常量x。类似的第二个case中,case (0,let y)匹配所有x值是0的点并且将改点的纵坐标赋值给常量y。
一旦常量被定义,他们就可以在case块中使用。例子中将他们的值打印了出来。
这个例子中switch没有default分支,最后的case中, case let (x,y),定义了一个元组并且将会匹配任何点。结果就是这个case会匹配所有剩下的情况,所以没有必要使用default确保switch包含了全部情形。
上面例子中定义x和y使用let关键字,这是因为在case中不需要对其做修改。当然也可以使用var将他们定义为变量,case块中就可以对其进行修改了。

where

可以在switch的case语句中使用where添加附加的判断条件。
下面的例子还是对点在图形上做归类:

let​ ​yetAnotherPoint​ = (​1​,-​1​)
​switch​ ​yetAnotherPoint​ {
​case​ ​let​ (​x​,​y​) ​where​ ​x​ == ​y​:
​ ​println​(​"(​\(​x​)​,​\(​y​)​) is on the line x == y"​)
​case​ ​let​ (​x​,​y​) ​where​ ​x​ == -​y​:
​ ​println​(​"(​\(​x​)​,​\(​y​)​) is on the line x == -y"​)
​case​ ​let​ (​x​,​y​):
​ ​println​(​"(​\(​x​)​,​\(​y​)​) is just some arbitrary point"​)
​}
​// prints "(1,-1) is on the line x == -y"

这个例子,switch判断点是在绿色的线上、 紫色的线上还是其他。
case语句中定义了x和y常量,分别绑定yetAnotherPoint元组中的两个元素。这两个常量被用在了where语句中,作为动态过滤条件。只有当过滤条件满足的时候,才会匹配到对应的case。
上面例子中,最后一个case匹配所有情况了,所以default就不需要了。

控制转移语句

四个转移语句:
continue
break
fallthrough
return
continue,break,和fallthrough 会在下文说明。return语句参见Function一节。

continue

continue告诉循环要停止现在做的事情,返回循环体开始,开始下一个循环。也就是说:我做完了本次循环。不离开循环。
NOTE
for循环有条件和自增,循环的自增在调用了cntinue之后仍会被执行。循环自身继续执行,只有循环体内的代码会被忽略。

下面的例子移除了一个小写字符串中的元音字母和空格,形成了一个字谜:

let​ ​puzzleInput​ = ​"great minds think alike"
​var​ ​puzzleOutput​ = ​""
​for​ ​character​ ​in​ ​puzzleInput​ {
​ ​switch​ ​character​ {
​ ​case​ ​"a"​,​"u"​,​" "​:
​ ​continue
​ ​default​:
​ ​puzzleOutput​.​append​(​character​)
​ }
​}
​println​(​puzzleOutput​)
​// prints "grtmndsthnklk"

上面的代码在匹配到元音字母和空格时使用了continue关键字,导致当前循环直接进行到最后并且返回到循环体的开始进行下一次循环。这样让switch块匹配到元音字母和空格,不必将其他字母输出。

break

break会立即结束完整的控制流。break可以在switch语句或者循环语句中使用,当你在上述结构中想要终止继续执行的时候。

循环中的break

循环中使用break,直接结束循环,循环结束标记最后一个}后的第一行将被继续执行。当前循环的后续内容不被执行,后续循环的内容也不被执行。

switch中的break

switch中使用break会导致switch内容不再继续被执行,控制权交由switch末尾}后的第一行。

这个行为可以在想要忽略一个或多个case的时候使用。 因为Swift的switch要包含全部情形而且不允许有空的case存在,所以在需要直接表述特意匹配或者忽略某个case的时候特别有用。这样的具体作法是在case块中写break。当这个case被匹配到后,直接结束switch。
NOTE
switch的case中如果至于一条注释将会报错。注释并不是语句而且不能让这个case被忽略。要忽略一个case需要使用break。
下面的例子检查一个字符的值,判断是否返回一个数字标记(对应四种语言)。多个值在switch中就这样被简介的处理了:

let​ ​numberSymbol​: ​Character​ = ​"三"​ ​// Simplified Chinese for the number 3
​var​ ​possibleIntegerValue​: ​Int​?
​switch​ ​numberSymbol​ {
​case​ ​"1"​,​"١"​,​"一"​,​"๑"​:
​ ​possibleIntegerValue​ = ​1
​case​ ​"2"​,​"٢"​,​"二"​,​"๒"​:
​ ​possibleIntegerValue​ = ​2
​case​ ​"3"​,​"٣"​,​"三"​,​"๓"​:
​ ​possibleIntegerValue​ = ​3
​case​ ​"4"​,​"٤"​,​"四"​,​"๔"​:
​ ​possibleIntegerValue​ = ​4
​default​:
​ ​break
​}
​if​ ​let​ ​integerValue​ = ​possibleIntegerValue​ {
​ ​println​(​"The integer value of ​\(​numberSymbol​)​ is ​\(​integerValue​)​."​)
​} ​else​ {
​ ​println​(​"An integer value could not be found for ​\(​numberSymbol​)​."​)
​}
​// prints "The integer value of 三 is 3."

这个例子会检查numberSymbol 判断它是拉丁语、阿拉伯语、汉语还是泰语的1-4数字。如果匹配到了,switch的一个case会给一个叫做possibleIntegerValue 的Int可选类型赋予恰当的值。
在switch执行完毕后,这个例子采用了可选绑定判断是不是找到了一个值。possibleIntegerValue 变量被默认赋值为nil,如果possibleIntegerValue 被赋予了值,则可选绑定成功。这个赋值操作是在switch的case中进行的。
因为没有应对所有的字符情况,所以一个default case被采用了,用来匹配所有其他的字符。这个default里面不需要执行任何语句,所以写了一个break。当default被匹配到,break就结束了整个switch,后续的if let 语句被执行。

接续执行(Fallthrough)

Swift的switch不依次接续执行case,而是指执行匹配到的第一个case就结束了整个switch。而C语言中你需要在每个case的结束添加break阻止默认接续执行。避免默认接续执行意味着Swift的switch比C语言更加简明,这样避免了不小心执行多个case的情况。
如果你想要C风格的接续执行行为,你可以选择在swift的switch中使用 fallthrough 关键字。下面就是一个例子:

let​ ​integerToDescribe​ = ​5
​var​ ​description​ = ​"The number ​\(​integerToDescribe​)​ is"
​switch​ ​integerToDescribe​ {
​case​ ​2​,​3​,​5​,​7​,​11​,​13​,​17​,​19​:
​ ​description​ += ​" a prime number,and also"
​ ​fallthrough
​default​:
​ ​description​ += ​" an integer."
​}
​println​(​description​)
​// prints "The number 5 is a prime number,and also an integer."

这个例子定义了一个字符串类型的变量并且给他赋值了。然后用switch语句检查integerToDescribe 的值。如果integerToDescribe 是一个素数,integerToDescribe 会被追加素数的标记内容。接下来使用了fallthrough关键字,接续执行default case。default里面给integerToDescribe 追加了一些值。switch就执行完了。
如果integerToDescribe 的值不是列表中的素数,根本就不会走第一个case。因为没有其他 的情况,所以integerToDescribe 必然会匹配defalult。
所有的switch被执行,数字的表述被使用println方法打印出来。这个例子中5被正确的作为一个素数对待了。
NOTE
fallthrough关键字不检查接续执行的case的条件。fallthrough关键字能做到和C语言中的switch一样,执行完毕接续执行下一个case。

标签语句

用Swift你可以在循环和switch中嵌套使用循环和switch,构建复杂的控制流结构。循环和switch语句都可以使用break语句提前结束。然而更有用的是明确在break的对象到底是哪个循环或者switch。类似的在一个多重循环中,表明continue语句影响的是哪个循环也是有用的。

为了获得上述目标,你可以给循环或者switch添加标签,然后是使用这个标签在break或者continue语句中。
被标签的语句在开始使用标签的名字,后面跟冒号。这里有一个while循环的例子,switch也类似:

label name: while condition {

    statements

}

下面的例子使用break和continue以及添加了标签的while循环。案例还是前述的蛇和梯子的游戏。这次有了额外的规则:
你必须准确的到达25号格子才能赢。
如果你超过了25号格子,那么你必须重新来过直到你恰好到达25号格子。
游戏面板和以前一样:

finalSquare,and diceRoll 的值初始化方式和先前一样:
​let​ ​finalSquare​ = ​25
​var​ ​board​ = [​Int​](​count​: ​finalSquare​ + ​1​,​repeatedValue​: ​0​)
​board​[​03​] = +​08​; ​board​[​06​] = +​11​; ​board​[​09​] = +​09​; ​board​[​10​] = +​02
​board​[​14​] = -​10​; ​board​[​19​] = -​11​; ​board​[​22​] = -​02​; ​board​[​24​] = -​08
​var​ ​square​ = ​0
​var​ ​diceRoll​ = ​0

这个版本的游戏是哟给你了一个while循环和一个switch来实现游戏逻辑。while循环被添加了标签叫做gameLoop,表明这是游戏的主逻辑。
while循环的条件现在是while square != finalSquare,表明你必须恰好到25号格子才行:

gameLoop​: ​while​ ​square​ != ​finalSquare​ {
​ ​if​ ++​diceRoll​ == ​7​ { ​diceRoll​ = ​1​ }
​ ​switch​ ​square​ + ​diceRoll​ {
​ ​case​ ​finalSquare​:
​ ​// diceRoll will move us to the final square,so the game is over
​ ​break​ ​gameLoop
​ ​case​ ​let​ ​newSquare​ ​where​ ​newSquare​ > ​finalSquare​:
​ ​// diceRoll will move us beyond the final square,so roll again
​ ​continue​ ​gameLoop
​ ​default​:
​ ​// this is a valid move,so find out its effect
​ ​square​ += ​diceRoll
​ ​square​ += ​board​[​square​]
​ }
​}
​println​(​"Game over!"​)

每次循环都会首先掷色子。没有直接移动玩家,而是用一个switch检查移动的结果,检查移动是否合法:

  • 如果移动到最后一个格子,那么游戏结束。break gameLoop语句跳出while循环,开始执行其后第一句。
  • 如果移动后超过了最大格子,那么移动无效,玩家需要重新掷色子。continue gameLoop 结束当前的while循环,开始下一次循环。
  • 如果不是上面所述情况,移动就是有效的。玩家按照 色子点数移动,游戏逻辑会判断执行碰到梯子或是蛇的情形。这样下来当前的循环结束,返回 while的条件检查语句是否要继续下去。

NOTE 如果上面的break语句不使用gameLoop标签,将会跳出switch语句,而不是while语句。使用gameLoop标签使得要结束的语句更加明确。 这里其实并不一定要使用gameLoop标签,当使用continue gameLoop来跳转到下一次循环时。在这个游戏中只有一个层循环,所以只使用continue并不会有任何歧义。然而这里并不妨碍使用continue时使用gameLoop标签。这样的好处都是使得逻辑清晰易读易懂。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘贴.待开发的功能:1.支持自动生成约束2.开发设置页面3.做一个浏览器插件,支持不需要下载整个工程,可即时操作当前蓝湖浏览页面4.支持Flutter语言模板生成5.支持更多平台,如Sketch等6.支持用户自定义语言模板
现实生活中,我们听到的声音都是时间连续的,我们称为这种信号叫模拟信号。模拟信号需要进行数字化以后才能在计算机中使用。目前我们在计算机上进行音频播放都需要依赖于音频文件。那么音频文件如何生成的呢?音频文件的生成过程是将声音信息采样、量化和编码产生的数字信号的过程,我们人耳所能听到的声音频率范围为(20Hz~20KHz),因此音频文件格式的最大带宽是20KHZ。根据奈奎斯特的理论,音频文件的采样率一般在40~50KHZ之间。奈奎斯特采样定律,又称香农采样定律。...............
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿遍又亿遍,久久不能离开!看着小仙紫姐姐的蹦迪视频,除了一键三连还能做什么?突发奇想,能不能把舞蹈视频转成代码舞呢?说干就干,今天就手把手教大家如何把跳舞视频转成代码舞,跟着仙女姐姐一起蹦起来~视频来源:【紫颜】见过仙女蹦迪吗 【千盏】一、核心功能设计总体来说,我们需要分为以下几步完成:从B站上把小姐姐的视频下载下来对视频进行截取GIF,把截取的GIF通过ASCII Animator进行ASCII字符转换把转换的字符gif根据每
【Android App】实战项目之仿抖音的短视频分享App(附源码和演示视频 超详细必看)
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至2022年4月底。我已经将这篇博客的内容写为论文,上传至arxiv:https://arxiv.org/pdf/2204.10160.pdf欢迎大家指出我论文中的问题,特别是语法与用词问题在github上,我也上传了完整的项目:https://github.com/Whiffe/Custom-ava-dataset_Custom-Spatio-Temporally-Action-Video-Dataset关于自定义ava数据集,也是后台
因为我既对接过session、cookie,也对接过JWT,今年因为工作需要也对接了gtoken的2个版本,对这方面的理解还算深入。尤其是看到官方文档评论区又小伙伴表示看不懂,所以做了这期视频内容出来:视频在这里:本期内容对应B站的开源视频因为涉及的知识点比较多,视频内容比较长。如果你觉得看视频浪费时间,可以直接阅读源码:goframe v2版本集成gtokengoframe v1版本集成gtokengoframe v2版本集成jwtgoframe v2版本session登录官方调用示例文档jwt和sess
【Android App】实战项目之仿微信的私信和群聊App(附源码和演示视频 超详细必看)
用Android Studio的VideoView组件实现简单的本地视频播放器。本文将讲解如何使用Android视频播放器VideoView组件来播放本地视频和网络视频,实现起来还是比较简单的。VideoView组件的作用与ImageView类似,只是ImageView用于显示图片,VideoView用于播放视频。...
采用MATLAB对正弦信号,语音信号进行生成、采样和内插恢复,利用MATLAB工具箱对混杂噪声的音频信号进行滤波
随着移动互联网、云端存储等技术的快速发展,包含丰富信息的音频数据呈现几何级速率增长。这些海量数据在为人工分析带来困难的同时,也为音频认知、创新学习研究提供了数据基础。在本节中,我们通过构建生成模型来生成音频序列文件,从而进一步加深对序列数据处理问题的了解。
基于yolov5+deepsort+slowfast算法的视频实时行为检测。1. yolov5实现目标检测,确定目标坐标 2. deepsort实现目标跟踪,持续标注目标坐标 3. slowfast实现动作识别,并给出置信率 4. 用框持续框住目标,并将动作类别以及置信度显示在框上
数字电子钟设计本文主要完成数字电子钟的以下功能1、计时功能(24小时)2、秒表功能(一个按键实现开始暂停,另一个按键实现清零功能)3、闹钟功能(设置闹钟以及到时响10秒)4、校时功能5、其他功能(清零、加速、星期、八位数码管显示等)前排提示:前面几篇文章介绍过的内容就不详细介绍了,可以看我专栏的前几篇文章。PS.工程文件放在最后面总体设计本次设计主要是在前一篇文章 数字电子钟基本功能的实现 的基础上改编而成的,主要结构不变,分频器将50MHz分为较低的频率备用;dig_select
1.进入官网下载OBS stdioOpen Broadcaster Software | OBS (obsproject.com)2.下载一个插件,拓展OBS的虚拟摄像头功能链接:OBS 虚拟摄像头插件.zip_免费高速下载|百度网盘-分享无限制 (baidu.com)提取码:6656--来自百度网盘超级会员V1的分享**注意**该插件必须下载但OBS的根目录(应该是自动匹配了的)3.打开OBS,选中虚拟摄像头选择启用在底部添加一段视频录制选择下面,进行录制.
Meta公司在9月29日首次推出一款人工智能系统模型:Make-A-Video,可以从给定的文字提示生成短视频。基于**文本到图像生成技术的最新进展**,该技术旨在实现文本到视频的生成,可以仅用几个单词或几行文本生成异想天开、独一无二的视频,将无限的想象力带入生活
音频信号叠加噪声及滤波一、前言二、信号分析及加噪三、滤波去噪四、总结一、前言之前一直对硬件上的内容比较关注,但是可能是因为硬件方面的东西可能真的是比较杂,而且需要渗透的东西太多了,所以学习进展比较缓慢。因为也很少有单纯的硬件学习研究,总是会伴随着各种理论需要硬件做支撑,所以还是想要慢慢接触理论学习。但是之前总找不到切入点,不知道从哪里开始,就一直拖着。最近稍微接触了一点信号处理,就用这个当作切入点,开始接触理论学习。二、信号分析及加噪信号处理选用了matlab做工具,选了一个最简单的语音信号处理方
腾讯云 TRTC 实时音视频服务体验,从认识 TRTC 到 TRTC 的开发实践,Demo 演示& IM 服务搭建。
音乐音频分类技术能够基于音乐内容为音乐添加类别标签,在音乐资源的高效组织、检索和推荐等相关方面的研究和应用具有重要意义。传统的音乐分类方法大量使用了人工设计的声学特征,特征的设计需要音乐领域的知识,不同分类任务的特征往往并不通用。深度学习的出现给更好地解决音乐分类问题提供了新的思路,本文对基于深度学习的音乐音频分类方法进行了研究。首先将音乐的音频信号转换成声谱作为统一表示,避免了手工选取特征存在的问题,然后基于一维卷积构建了一种音乐分类模型。
C++知识精讲16 | 井字棋游戏(配资源+视频)【赋源码,双人对战】
本文主要讲解如何在Java中,使用FFmpeg进行视频的帧读取,并最终合并成Gif动态图。
在本篇博文中,我们谈及了 Swift 中 some、any 关键字以及主关联类型(primary associated types)的前世今生,并由浅及深用简明的示例向大家讲解了它们之间的奥秘玄机。