Swift提供了類似 C 語言的流程控制結(jié)構(gòu),包括可以多次執(zhí)行任務(wù)的for
和while
循環(huán),基于特定條件選擇執(zhí)行不同代碼分支的if
和switch
語句,還有控制流程跳轉(zhuǎn)到其他代碼的break
和continue
語句。
除了 C 語言里面?zhèn)鹘y(tǒng)的 for 條件遞增(for-condition-increment
)循環(huán),Swift 還增加了for-in
循環(huán),用來更簡單地遍歷數(shù)組(array),字典(dictionary),區(qū)間(range),字符串(string)和其他序列類型。
Swift 的switch
語句比 C 語言中更加強(qiáng)大。在 C 語言中,如果某個 case 不小心漏寫了break
,這個 case 就會貫穿(fallthrough)至下一個 case,Swift 無需寫break
,所以不會發(fā)生這種貫穿(fallthrough)的情況。case 還可以匹配更多的類型模式,包括區(qū)間匹配(range matching),元組(tuple)和特定類型的描述。switch
的 case 語句中匹配的值可以是由 case 體內(nèi)部臨時的常量或者變量決定,也可以由where
分句描述更復(fù)雜的匹配條件。
for
循環(huán)用來按照指定的次數(shù)多次執(zhí)行一系列語句。Swift 提供兩種for
循環(huán)形式:
for-in
用來遍歷一個區(qū)間(range),序列(sequence),集合(collection),系列(progression)里面所有的元素執(zhí)行一系列語句。for-condition-increment
)語句,用來重復(fù)執(zhí)行一系列語句直到達(dá)成特定條件達(dá)成,一般通過在每次循環(huán)完成后增加計數(shù)器的值來實現(xiàn)。你可以使用for-in
循環(huán)來遍歷一個集合里面的所有元素,例如由數(shù)字表示的區(qū)間、數(shù)組中的元素、字符串中的字符。
下面的例子用來輸出乘 5 乘法表前面一部分內(nèi)容:
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
例子中用來進(jìn)行遍歷的元素是一組使用閉區(qū)間操作符(...
)表示的從1
到5
的數(shù)字。index
被賦值為閉區(qū)間中的第一個數(shù)字(1
),然后循環(huán)中的語句被執(zhí)行一次。在本例中,這個循環(huán)只包含一個語句,用來輸出當(dāng)前index
值所對應(yīng)的乘 5 乘法表結(jié)果。該語句執(zhí)行后,index
的值被更新為閉區(qū)間中的第二個數(shù)字(2
),之后println
方法會再執(zhí)行一次。整個過程會進(jìn)行到閉區(qū)間結(jié)尾為止。
上面的例子中,index
是一個每次循環(huán)遍歷開始時被自動賦值的常量。這種情況下,index
在使用前不需要聲明,只需要將它包含在循環(huán)的聲明中,就可以對其進(jìn)行隱式聲明,而無需使用let
關(guān)鍵字聲明。
注意:
index
常量只存在于循環(huán)的生命周期里。如果你想在循環(huán)完成后訪問index
的值,又或者想讓index
成為一個變量而不是常量,你必須在循環(huán)之前自己進(jìn)行聲明。
如果你不需要知道區(qū)間內(nèi)每一項的值,你可以使用下劃線(_
)替代變量名來忽略對值的訪問:
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
println("\(base) to the power of \(power) is \(answer)")
// 輸出 "3 to the power of 10 is 59049"
這個例子計算 base 這個數(shù)的 power 次冪(本例中,是3
的10
次冪),從1
(3
的0
次冪)開始做3
的乘法, 進(jìn)行10
次,使用1
到10
的閉區(qū)間循環(huán)。這個計算并不需要知道每一次循環(huán)中計數(shù)器具體的值,只需要執(zhí)行了正確的循環(huán)次數(shù)即可。下劃線符號_
(替代循環(huán)中的變量)能夠忽略具體的值,并且不提供循環(huán)遍歷時對值的訪問。
使用for-in
遍歷一個數(shù)組所有元素:
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
println("Hello, \(name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!
你也可以通過遍歷一個字典來訪問它的鍵值對(key-value pairs)。遍歷字典時,字典的每項元素會以(key, value)
元組的形式返回,你可以在for-in
循環(huán)中使用顯式的常量名稱來解讀(key, value)
元組。下面的例子中,字典的鍵(key)解讀為常量animalName
,字典的值會被解讀為常量legCount
:
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
println("\(animalName)s have \(legCount) legs")
}
// spiders have 8 legs
// ants have 6 legs
// cats have 4 legs
字典元素的遍歷順序和插入順序可能不同,字典的內(nèi)容在內(nèi)部是無序的,所以遍歷元素時不能保證順序。關(guān)于數(shù)組和字典,詳情參見集合類型。
除了數(shù)組和字典,你也可以使用for-in
循環(huán)來遍歷字符串中的字符(Character
):
for character in "Hello" {
println(character)
}
// H
// e
// l
// l
// o
除了for-in
循環(huán),Swift 提供使用條件判斷和遞增方法的標(biāo)準(zhǔn) C 樣式for
循環(huán):
for var index = 0; index < 3; ++index {
println("index is \(index)")
}
// index is 0
// index is 1
// index is 2
下面是一般情況下這種循環(huán)方式的格式:
for
initialization
;condition
;increment
{statements
}
和 C 語言中一樣,分號將循環(huán)的定義分為 3 個部分,不同的是,Swift 不需要使用圓括號將“initialization; condition; increment”包括起來。
這個循環(huán)執(zhí)行流程如下:
false
,循環(huán)結(jié)束,繼續(xù)執(zhí)行for
循環(huán)關(guān)閉大括號(}
)之后的代碼。如果表達(dá)式調(diào)用結(jié)果為true
,則會執(zhí)行大括號內(nèi)部的代碼(statements)。上述描述和循環(huán)格式等同于:
initialization
whilecondition
{statements
increment
}
在初始化表達(dá)式中聲明的常量和變量(比如var index = 0
)只在for
循環(huán)的生命周期里有效。如果想在循環(huán)結(jié)束后訪問index
的值,你必須要在循環(huán)生命周期開始前聲明index
。
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")
// 輸出 "The loop statements were executed 3 times
注意index
在循環(huán)結(jié)束后最終的值是3
而不是2
。最后一次調(diào)用遞增表達(dá)式++index
會將index
設(shè)置為3
,從而導(dǎo)致index < 3
條件為false
,并終止循環(huán)。
while
循環(huán)運行一系列語句直到條件變成false
。這類循環(huán)適合使用在第一次迭代前迭代次數(shù)未知的情況下。Swift 提供兩種while
循環(huán)形式:
while
循環(huán),每次在循環(huán)開始時計算條件是否符合;do-while
循環(huán),每次在循環(huán)結(jié)束時計算條件是否符合。while
循環(huán)從計算單一條件開始。如果條件為true
,會重復(fù)運行一系列語句,直到條件變?yōu)?code>false。
下面是一般情況下 while
循環(huán)格式:
while
condition
{statements
}
下面的例子來玩一個叫做_蛇和梯子(Snakes and Ladders)_的小游戲,也叫做滑道和梯子(Chutes and Ladders):
游戲的規(guī)則如下:
游戲盤面可以使用一個Int
數(shù)組來表達(dá)。數(shù)組的長度由一個finalSquare
常量儲存,用來初始化數(shù)組和檢測最終勝利條件。游戲盤面由 26 個 Int
0 值初始化,而不是 25 個(由0
到25
,一共 26 個):
let finalSquare = 25
var board = [Int](count: finalSquare + 1, repeatedValue: 0)
一些方塊被設(shè)置成有蛇或者梯子的指定值。梯子底部的方塊是一個正值,使你可以向上移動,蛇頭處的方塊是一個負(fù)值,會讓你向下移動:
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 號方格,我們使用board[03]
等于+08
(來表示11
和3
之間的差值)。使用一元加運算符(+i
)是為了和一元減運算符(-i
)對稱,為了讓盤面代碼整齊,小于 10 的數(shù)字都使用 0 補(bǔ)齊(這些風(fēng)格上的調(diào)整都不是必須的,只是為了讓代碼看起來更加整潔)。
玩家由左下角編號為 0 的方格開始游戲。一般來說玩家第一次擲骰子后才會進(jìn)入游戲盤面:
var square = 0
var diceRoll = 0
while square < finalSquare {
// 擲骰子
if ++diceRoll == 7 { diceRoll = 1 }
// 根據(jù)點數(shù)移動
square += diceRoll
if square < board.count {
// 如果玩家還在棋盤上,順著梯子爬上去或者順著蛇滑下去
square += board[square]
}
}
println("Game over!")
本例中使用了最簡單的方法來模擬擲骰子。 diceRoll
的值并不是一個隨機(jī)數(shù),而是以0
為初始值,之后每一次while
循環(huán),diceRoll
的值使用前置自增操作符(++i
)來自增 1 ,然后檢測是否超出了最大值。++diceRoll
調(diào)用完成后,返回值等于diceRoll
自增后的值。任何時候如果diceRoll
的值等于7時,就超過了骰子的最大值,會被重置為1
。所以diceRoll
的取值順序會一直是1
,2
,3
,4
,5
,6
,1
,2
。
擲完骰子后,玩家向前移動diceRoll
個方格,如果玩家移動超過了第 25 個方格,這個時候游戲結(jié)束,相應(yīng)地,代碼會在square
增加board[square]
的值向前或向后移動(遇到了梯子或者蛇)之前,檢測square
的值是否小于board
的count
屬性。
如果沒有這個檢測(square < board.count
),board[square]
可能會越界訪問board
數(shù)組,導(dǎo)致錯誤。例如如果square
等于26
, 代碼會去嘗試訪問board[26]
,超過數(shù)組的長度。
當(dāng)本輪while
循環(huán)運行完畢,會再檢測循環(huán)條件是否需要再運行一次循環(huán)。如果玩家移動到或者超過第 25 個方格,循環(huán)條件結(jié)果為false
,此時游戲結(jié)束。
while
循環(huán)比較適合本例中的這種情況,因為在 while
循環(huán)開始時,我們并不知道游戲的長度或者循環(huán)的次數(shù),只有在達(dá)成指定條件時循環(huán)才會結(jié)束。
while
循環(huán)的另外一種形式是do-while
,它和while
的區(qū)別是在判斷循環(huán)條件之前,先執(zhí)行一次循環(huán)的代碼塊,然后重復(fù)循環(huán)直到條件為false
。
下面是一般情況下 do-while
循環(huán)的格式:
do {
statements
} whilecondition
還是蛇和梯子的游戲,使用do-while
循環(huán)來替代while
循環(huán)。finalSquare
、board
、square
和diceRoll
的值初始化同while
循環(huán)一樣:
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
do-while
的循環(huán)版本,循環(huán)中_第一步_就需要去檢測是否在梯子或者蛇的方塊上。沒有梯子會讓玩家直接上到第 25 個方格,所以玩家不會通過梯子直接贏得游戲。這樣在循環(huán)開始時先檢測是否踩在梯子或者蛇上是安全的。
游戲開始時,玩家在第 0 個方格上,board[0]
一直等于 0, 不會有什么影響:
do {
// 順著梯子爬上去或者順著蛇滑下去
square += board[square]
// 擲骰子
if ++diceRoll == 7 { diceRoll = 1 }
// 根據(jù)點數(shù)移動
square += diceRoll
} while square < finalSquare
println("Game over!")
檢測完玩家是否踩在梯子或者蛇上之后,開始擲骰子,然后玩家向前移動diceRoll
個方格,本輪循環(huán)結(jié)束。
循環(huán)條件(while square < finalSquare
)和while
方式相同,但是只會在循環(huán)結(jié)束后進(jìn)行計算。在這個游戲中,do-while
表現(xiàn)得比while
循環(huán)更好。do-while
方式會在條件判斷square
沒有超出后直接運行square += board[square]
,這種方式可以去掉while
版本中的數(shù)組越界判斷。
根據(jù)特定的條件執(zhí)行特定的代碼通常是十分有用的,例如:當(dāng)錯誤發(fā)生時,你可能想運行額外的代碼;或者,當(dāng)輸入的值太大或太小時,向用戶顯示一條消息等。要實現(xiàn)這些功能,你就需要使用條件語句。
Swift 提供兩種類型的條件語句:if
語句和switch
語句。通常,當(dāng)條件較為簡單且可能的情況很少時,使用if
語句。而switch
語句更適用于條件較復(fù)雜、可能情況較多且需要用到模式匹配(pattern-matching)的情境。
if
語句最簡單的形式就是只包含一個條件,當(dāng)且僅當(dāng)該條件為true
時,才執(zhí)行相關(guān)代碼:
var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.")
}
// 輸出 "It's very cold. Consider wearing a scarf."
上面的例子會判斷溫度是否小于等于 32 華氏度(水的冰點)。如果是,則打印一條消息;否則,不打印任何消息,繼續(xù)執(zhí)行if
塊后面的代碼。
當(dāng)然,if
語句允許二選一,也就是當(dāng)條件為false
時,執(zhí)行 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.")
}
// 輸出 "It's not that cold. Wear a t-shirt."
顯然,這兩條分支中總有一條會被執(zhí)行。由于溫度已升至 40 華氏度,不算太冷,沒必要再圍圍巾——因此,else
分支就被觸發(fā)了。
你可以把多個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.")
}
// 輸出 "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.")
}
在這個例子中,由于既不冷也不熱,所以不會觸發(fā)if
或else if
分支,也就不會打印任何消息。
switch
語句會嘗試把某個值與若干個模式(pattern)進(jìn)行匹配。根據(jù)第一個匹配成功的模式,switch
語句會執(zhí)行對應(yīng)的代碼。當(dāng)有可能的情況較多時,通常用switch
語句替換if
語句。
switch
語句最簡單的形式就是把某個值與一個或若干個相同類型的值作比較:
switch
some value to consider
{
casevalue 1
:respond to value 1
casevalue 2
,value 3
:respond to value 2 or 3
default:otherwise, do something else
}
switch
語句都由多個 case 構(gòu)成。為了匹配某些更特定的值,Swift 提供了幾種更復(fù)雜的匹配模式,這些模式將在本節(jié)的稍后部分提到。
每一個 case 都是代碼執(zhí)行的一條分支,這與if
語句類似。與之不同的是,switch
語句會決定哪一條分支應(yīng)該被執(zhí)行。
switch
語句必須是完備的。這就是說,每一個可能的值都必須至少有一個 case 分支與之對應(yīng)。在某些不可能涵蓋所有值的情況下,你可以使用默認(rèn)(default
)分支滿足該要求,這個默認(rèn)分支必須在switch
語句的最后面。
下面的例子使用switch
語句來匹配一個名為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")
}
// 輸出 "e is a vowel"
在這個例子中,第一個 case 分支用于匹配五個元音,第二個 case 分支用于匹配所有的輔音。
由于為其它可能的字符寫 case 分支沒有實際的意義,因此在這個例子中使用了默認(rèn)分支來處理剩下的既不是元音也不是輔音的字符——這就保證了switch
語句的完備性。
與 C 語言和 Objective-C 中的switch
語句不同,在 Swift 中,當(dāng)匹配的 case 分支中的代碼執(zhí)行完畢后,程序會終止switch
語句,而不會繼續(xù)執(zhí)行下一個 case 分支。這也就是說,不需要在 case 分支中顯式地使用break
語句。這使得switch
語句更安全、更易用,也避免了因忘記寫break
語句而產(chǎn)生的錯誤。
注意:
你依然可以在 case 分支中的代碼執(zhí)行完畢前跳出,詳情請參考Switch 語句中的 break。
每一個 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 語言里的switch
語句,在 Swift 中,switch
語句不會同時匹配"a"
和"A"
。相反的,上面的代碼會引起編譯期錯誤:case "a": does not contain any executable statements
——這就避免了意外地從一個 case 分支貫穿到另外一個,使得代碼更安全、也更直觀。
一個 case 也可以包含多個模式,用逗號把它們分開(如果太長了也可以分行寫):
switch
some value to consider
{
casevalue 1
,value 2
:statements
}注意:
如果想要貫穿至特定的 case 分支中,請使用fallthrough
語句,詳情請參考貫穿(Fallthrough)。
case 分支的模式也可以是一個值的區(qū)間。下面的例子展示了如何使用區(qū)間匹配來輸出任意數(shù)字對應(yīng)的自然語言格式:
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).")
// 輸出 "There are millions and millions of stars in the Milky Way."
你可以使用元組在同一個switch
語句中測試多個值。元組中的元素可以是值,也可以是區(qū)間。另外,使用下劃線(_
)來匹配所有可能的值。
下面的例子展示了如何使用一個(Int, Int)
類型的元組來分類下圖中的點(x, y):
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")
}
// 輸出 "(1, 1) is inside the box"
在上面的例子中,switch
語句會判斷某個點是否是原點(0, 0),是否在紅色的x軸上,是否在黃色y軸上,是否在一個以原點為中心的4x4的矩形里,或者在這個矩形外面。
不像 C 語言,Swift 允許多個 case 匹配同一個值。實際上,在這個例子中,點(0, 0)可以匹配所有四個 case。但是,如果存在多個匹配,那么只會執(zhí)行第一個被匹配到的 case 分支??紤]點(0, 0)會首先匹配case (0, 0)
,因此剩下的能夠匹配(0, 0)的 case 分支都會被忽視掉。
case 分支的模式允許將匹配的值綁定到一個臨時的常量或變量,這些常量或變量在該 case 分支里就可以被引用了——這種行為被稱為值綁定(value binding)。
下面的例子展示了如何在一個(Int, Int)
類型的元組中使用值綁定來分類下圖中的點(x, y):
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))")
}
// 輸出 "on the x-axis with an x value of 2"
在上面的例子中,switch
語句會判斷某個點是否在紅色的x軸上,是否在黃色y軸上,或者不在坐標(biāo)軸上。
這三個 case 都聲明了常量x
和y
的占位符,用于臨時獲取元組anotherPoint
的一個或兩個值。第一個 case ——case (let x, 0)
將匹配一個縱坐標(biāo)為0
的點,并把這個點的橫坐標(biāo)賦給臨時的常量x
。類似的,第二個 case ——case (0, let y)
將匹配一個橫坐標(biāo)為0
的點,并把這個點的縱坐標(biāo)賦給臨時的常量y
。
一旦聲明了這些臨時的常量,它們就可以在其對應(yīng)的 case 分支里引用。在這個例子中,它們用于簡化println
的書寫。
請注意,這個switch
語句不包含默認(rèn)分支。這是因為最后一個 case ——case let(x, y)
聲明了一個可以匹配余下所有值的元組。這使得switch
語句已經(jīng)完備了,因此不需要再書寫默認(rèn)分支。
在上面的例子中,x
和y
是常量,這是因為沒有必要在其對應(yīng)的 case 分支中修改它們的值。然而,它們也可以是變量——程序?qū)?chuàng)建臨時變量,并用相應(yīng)的值初始化它。修改這些變量只會影響其對應(yīng)的 case 分支。
case 分支的模式可以使用where
語句來判斷額外的條件。
下面的例子把下圖中的點(x, y)進(jìn)行了分類:
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")
}
// 輸出 "(1, -1) is on the line x == -y"
在上面的例子中,switch
語句會判斷某個點是否在綠色的對角線x == y
上,是否在紫色的對角線x == -y
上,或者不在對角線上。
這三個 case 都聲明了常量x
和y
的占位符,用于臨時獲取元組yetAnotherPoint
的兩個值。這些常量被用作where
語句的一部分,從而創(chuàng)建一個動態(tài)的過濾器(filter)。當(dāng)且僅當(dāng)where
語句的條件為true
時,匹配到的 case 分支才會被執(zhí)行。
就像是值綁定中的例子,由于最后一個 case 分支匹配了余下所有可能的值,switch
語句就已經(jīng)完備了,因此不需要再書寫默認(rèn)分支。
控制轉(zhuǎn)移語句改變你代碼的執(zhí)行順序,通過它你可以實現(xiàn)代碼的跳轉(zhuǎn)。Swift有四種控制轉(zhuǎn)移語句。
我們將會在下面討論continue
、break
和fallthrough
語句。return
語句將會在函數(shù)章節(jié)討論。
continue
語句告訴一個循環(huán)體立刻停止本次循環(huán)迭代,重新開始下次循環(huán)迭代。就好像在說“本次循環(huán)迭代我已經(jīng)執(zhí)行完了”,但是并不會離開整個循環(huán)體。
注意:
在一個for條件遞增(for-condition-increment
)循環(huán)體中,在調(diào)用continue
語句后,迭代增量仍然會被計算求值。循環(huán)體繼續(xù)像往常一樣工作,僅僅只是循環(huán)體中的執(zhí)行代碼會被跳過。
下面的例子把一個小寫字符串中的元音字母和空格字符移除,生成了一個含義模糊的短句:
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput {
switch character {
case "a", "e", "i", "o", "u", " ":
continue
default:
puzzleOutput.append(character)
}
}
println(puzzleOutput)
// 輸出 "grtmndsthnklk"
在上面的代碼中,只要匹配到元音字母或者空格字符,就調(diào)用continue
語句,使本次循環(huán)迭代結(jié)束,從新開始下次循環(huán)迭代。這種行為使switch
匹配到元音字母和空格字符時不做處理,而不是讓每一個匹配到的字符都被打印。
break
語句會立刻結(jié)束整個控制流的執(zhí)行。當(dāng)你想要更早的結(jié)束一個switch
代碼塊或者一個循環(huán)體時,你都可以使用break
語句。
當(dāng)在一個循環(huán)體中使用break
時,會立刻中斷該循環(huán)體的執(zhí)行,然后跳轉(zhuǎn)到表示循環(huán)體結(jié)束的大括號(}
)后的第一行代碼。不會再有本次循環(huán)迭代的代碼被執(zhí)行,也不會再有下次的循環(huán)迭代產(chǎn)生。
當(dāng)在一個switch
代碼塊中使用break
時,會立即中斷該switch
代碼塊的執(zhí)行,并且跳轉(zhuǎn)到表示switch
代碼塊結(jié)束的大括號(}
)后的第一行代碼。
這種特性可以被用來匹配或者忽略一個或多個分支。因為 Swift 的switch
需要包含所有的分支而且不允許有為空的分支,有時為了使你的意圖更明顯,需要特意匹配或者忽略某個分支。那么當(dāng)你想忽略某個分支時,可以在該分支內(nèi)寫上break
語句。當(dāng)那個分支被匹配到時,分支內(nèi)的break
語句立即結(jié)束switch
代碼塊。
注意:
當(dāng)一個switch
分支僅僅包含注釋時,會被報編譯時錯誤。注釋不是代碼語句而且也不能讓switch
分支達(dá)到被忽略的效果。你總是可以使用break
來忽略某個分支。
下面的例子通過switch
來判斷一個Character
值是否代表下面四種語言之一。為了簡潔,多個值被包含在了同一個分支情況中。
let numberSymbol: Character = "三" // 簡體中文里的數(shù)字 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).")
}
// 輸出 "The integer value of 三 is 3."
這個例子檢查numberSymbol
是否是拉丁,阿拉伯,中文或者泰語中的1
到4
之一。如果被匹配到,該switch
分支語句給Int?
類型變量possibleIntegerValue
設(shè)置一個整數(shù)值。
當(dāng)switch
代碼塊執(zhí)行完后,接下來的代碼通過使用可選綁定來判斷possibleIntegerValue
是否曾經(jīng)被設(shè)置過值。因為是可選類型的緣故,possibleIntegerValue
有一個隱式的初始值nil
,所以僅僅當(dāng)possibleIntegerValue
曾被switch
代碼塊的前四個分支中的某個設(shè)置過一個值時,可選的綁定將會被判定為成功。
在上面的例子中,想要把Character
所有的的可能性都枚舉出來是不現(xiàn)實的,所以使用default
分支來包含所有上面沒有匹配到字符的情況。由于這個default
分支不需要執(zhí)行任何動作,所以它只寫了一條break
語句。一旦落入到default
分支中后,break
語句就完成了該分支的所有代碼操作,代碼繼續(xù)向下,開始執(zhí)行if let
語句。
Swift 中的switch
不會從上一個 case 分支落入到下一個 case 分支中。相反,只要第一個匹配到的 case 分支完成了它需要執(zhí)行的語句,整個switch
代碼塊完成了它的執(zhí)行。相比之下,C 語言要求你顯示的插入break
語句到每個switch
分支的末尾來阻止自動落入到下一個 case 分支中。Swift 的這種避免默認(rèn)落入到下一個分支中的特性意味著它的switch
功能要比 C 語言的更加清晰和可預(yù)測,可以避免無意識地執(zhí)行多個 case 分支從而引發(fā)的錯誤。
如果你確實需要 C 風(fēng)格的貫穿(fallthrough)的特性,你可以在每個需要該特性的 case 分支中使用fallthrough
關(guān)鍵字。下面的例子使用fallthrough
來創(chuàng)建一個數(shù)字的描述語句。
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)
// 輸出 "The number 5 is a prime number, and also an integer."
這個例子定義了一個String
類型的變量description
并且給它設(shè)置了一個初始值。函數(shù)使用switch
邏輯來判斷integerToDescribe
變量的值。當(dāng)integerToDescribe
的值屬于列表中的質(zhì)數(shù)之一時,該函數(shù)添加一段文字在description
后,來表明這個是數(shù)字是一個質(zhì)數(shù)。然后它使用fallthrough
關(guān)鍵字來“貫穿”到default
分支中。default
分支添加一段額外的文字在description
的最后,至此switch
代碼塊執(zhí)行完了。
如果integerToDescribe
的值不屬于列表中的任何質(zhì)數(shù),那么它不會匹配到第一個switch
分支。而這里沒有其他特別的分支情況,所以integerToDescribe
匹配到包含所有的default
分支中。
當(dāng)switch
代碼塊執(zhí)行完后,使用println
函數(shù)打印該數(shù)字的描述。在這個例子中,數(shù)字5
被準(zhǔn)確的識別為了一個質(zhì)數(shù)。
注意:
fallthrough
關(guān)鍵字不會檢查它下一個將會落入執(zhí)行的 case 中的匹配條件。fallthrough
簡單地使代碼執(zhí)行繼續(xù)連接到下一個 case 中的執(zhí)行代碼,這和 C 語言標(biāo)準(zhǔn)中的switch
語句特性是一樣的。
在 Swift 中,你可以在循環(huán)體和switch
代碼塊中嵌套循環(huán)體和switch
代碼塊來創(chuàng)造復(fù)雜的控制流結(jié)構(gòu)。然而,循環(huán)體和switch
代碼塊兩者都可以使用break
語句來提前結(jié)束整個方法體。因此,顯示地指明break
語句想要終止的是哪個循環(huán)體或者switch
代碼塊,會很有用。類似地,如果你有許多嵌套的循環(huán)體,顯示指明continue
語句想要影響哪一個循環(huán)體也會非常有用。
為了實現(xiàn)這個目的,你可以使用標(biāo)簽來標(biāo)記一個循環(huán)體或者switch
代碼塊,當(dāng)使用break
或者continue
時,帶上這個標(biāo)簽,可以控制該標(biāo)簽代表對象的中斷或者執(zhí)行。
產(chǎn)生一個帶標(biāo)簽的語句是通過在該語句的關(guān)鍵詞的同一行前面放置一個標(biāo)簽,并且該標(biāo)簽后面還需帶著一個冒號。下面是一個while
循環(huán)體的語法,同樣的規(guī)則適用于所有的循環(huán)體和switch
代碼塊。
label name
: whilecondition
{statements
}
下面的例子是在一個帶有標(biāo)簽的while
循環(huán)體中調(diào)用break
和continue
語句,該循環(huán)體是前面章節(jié)中_蛇和梯子_的改編版本。這次,游戲增加了一條額外的規(guī)則:
如果某次擲骰子使你的移動超出第 25 個方塊,你必須重新擲骰子,直到你擲出的骰子數(shù)剛好使你能落在第 25 個方塊中。
游戲的棋盤和之前一樣:
值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
這個版本的游戲使用while
循環(huán)體和switch
方法塊來實現(xiàn)游戲的邏輯。while
循環(huán)體有一個標(biāo)簽名gameLoop
,來表明它是蛇與梯子的主循環(huán)。
該while
循環(huán)體的條件判斷語句是while square !=finalSquare
,這表明你必須剛好落在方格25中。
gameLoop: while square != finalSquare {
if ++diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 到達(dá)最后一個方塊,游戲結(jié)束
break gameLoop
case let newSquare where newSquare > finalSquare:
// 超出最后一個方塊,再擲一次骰子
continue gameLoop
default:
// 本次移動有效
square += diceRoll
square += board[square]
}
}
println("Game over!")
每次循環(huán)迭代開始時擲骰子。與之前玩家擲完骰子就立即移動不同,這里使用了switch
來考慮每次移動可能產(chǎn)生的結(jié)果,從而決定玩家本次是否能夠移動。
break gameLoop
語句跳轉(zhuǎn)控制去執(zhí)行while
循環(huán)體后的第一行代碼,游戲結(jié)束。continue gameLoop
語句結(jié)束本次while
循環(huán)的迭代,開始下一次循環(huán)迭代。while
循環(huán)體的條件判斷語句處,再決定是否能夠繼續(xù)執(zhí)行下次循環(huán)迭代。注意:
如果上述的break
語句沒有使用gameLoop
標(biāo)簽,那么它將會中斷switch
代碼塊而不是while
循環(huán)體。使用gameLoop
標(biāo)簽清晰的表明了break
想要中斷的是哪個代碼塊。同時請注意,當(dāng)調(diào)用continue gameLoop
去跳轉(zhuǎn)到下一次循環(huán)迭代時,這里使用gameLoop
標(biāo)簽并不是嚴(yán)格必須的。因為在這個游戲中,只有一個循環(huán)體,所以continue
語句會影響到哪個循環(huán)體是沒有歧義的。然而,continue
語句使用gameLoop
標(biāo)簽也是沒有危害的。這樣做符合標(biāo)簽的使用規(guī)則,同時參照旁邊的break gameLoop
,能夠使游戲的邏輯更加清晰和易于理解。
更多建議: