Swift提供了所有C語言中相似的控制流程結構。包括for和while迴圈;if和switch條件陳述式;break和continue跳躍陳述式等。
Swift還加入了for-in迴圈語句,讓編程人員可以在遍曆數組,字典,範圍,字串或者其它序列時更加便捷。
相對於C語言,Swift中switch語句的case語句後,不會自動跳轉到下一個語句,這樣就避免了C語言中因為忘記break而造成的錯誤。另外case語句可以匹配多種類型,包括資料範圍,元組,或者特定的類型等。switch語句中已匹配的數值也可以被用在後續的case語句體中,where關鍵詞還能被加入任意的case語句中,來增加匹配的方式。
1、for迴圈
for迴圈可以根據設定,重複執行一個代碼塊多次。Swift中提供了兩種for迴圈方式:
for-in迴圈,對於資料範圍,序列,集合等中的每一個元素,都執行一次
for-condition-increment,一直執行,知道一個特定的條件滿足,每一次迴圈執行,都會增加一次計數
for-in迴圈
下面的例子列印出了5的倍數序列的前5項
複製代碼 代碼如下:
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,然後執行迴圈體中的代碼。在這種情況下,迴圈只有一條語句,也就是列印5的index倍數。在這條語句執行完畢後,index的值被更新為序列中的下一個數值2,println函數再次被調用,一次迴圈直到這個序列的結尾。
在上面的例子中,index在每一次迴圈開始前都已經被賦值,因此不需要在每次使用前對它進行定義。每次它都隱式地被定義,就像是使用了let關鍵詞一樣。注意index是一個常量。
注意:index只在迴圈中存在,在迴圈完成之後如果需要繼續使用,需要重新定義才可以。
如果你不需要序列中的每一個值,可以使用_來忽略它,僅僅只是使用迴圈體本身:
複製代碼 代碼如下:
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次方)。連續的乘法從1(實際上是3的0次方)開始,依次累乘以3,由於使用的是半閉區間,從0開始到9的左閉右開區間,所以是執行10次。在迴圈的時候不需要知道實際執行到第一次了,而是要保證執行了正確的次數,因此這裡不需要index的值。
同理我們可以使用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對來進行遍曆。每一個字典中的元素都是一個(key, value)元組,當遍曆的時候,可以指定欄位的key和value為一個特定的名稱,這樣在遍曆的時候就可以更好地理解和使用它們,比如下面例子中的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
字典中的元素在遍曆的時候一般不需要按照插入的順序,因此不能保證遍曆字典的時候,元素是有序的。更多跟數組和字典相關的內容可以參考:Collection Types
另外在數組和字典中也可以使用類似的遍曆方式,如可以使用for-in迴圈來遍曆字串中的每一個字元:
複製代碼 代碼如下:
for character in "Hello" {
println(character)
}
// H
// e
// l
// l
// o
For-Condition-Increment條件迴圈
Swift同樣支援C語言樣式的for迴圈,它也包括了一個條件陳述式和一個增量語句:
複製代碼 代碼如下:
for var index = 0; index < 3; ++index {
println("index is \(index)")
}
// index is 0
// index is 1
// index is 2
下面是這種for迴圈的一般結構:
複製代碼 代碼如下:
for initialization; condition; increment {
statements
}
分號在這裡用來分隔for迴圈的三個結構,和C語言一樣,但是不需要用括弧來包裹它們。
這種for迴圈的執行方式是:
1、當進入迴圈的時候,初始化語句首先被執行,設定好迴圈需要的變數或常量
2、測試條件語句,看是否滿足繼續迴圈的條件,只有在條件陳述式是true的時候才會繼續執行,如果是false則會停止迴圈。
3、在所有的迴圈體語句執行完畢後,增量語句執行,可能是對計數器的增加或者是減少,或者是其它的一些語句。然後返回步驟2繼續執行。
這種迴圈方式還可以被描述為下面的形式:
複製代碼 代碼如下:
initialization
while condition {
statements
increment
}
在初始化語句中被定義(比如var index = 0)的常量和變數,只在for迴圈語句範圍內有效。如果想要在迴圈執行之後繼續使用,需要在迴圈開始之前就定義好:
複製代碼 代碼如下:
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增1之後,條件陳述式index < 3返回false,迴圈才終止,而這時,index已經為3了。
2、while迴圈
while迴圈執行一系列代碼塊,直到某個條件為false為止。這種迴圈最長用於迴圈的次數不確定的情況。Swift提供了兩種while迴圈方式:
while迴圈,在每次迴圈開始前測試迴圈條件是否成立
do-while迴圈,在每次迴圈之後測試迴圈條件是否成立
while迴圈
while迴圈由一個條件陳述式開始,如果條件陳述式為true,一直執行,直到條件陳述式變為false。下面是一個while迴圈的一般形式:
複製代碼 代碼如下:
while condition {
statements
}
下面的例子是一個簡單的遊戲,Snakes and Ladders,蛇和梯子:
遊戲的規則是這樣的:
遊戲面板上有25個格子,遊戲的目標是到達第25個格子;
每個回合通過一個6面的骰子來決定行走的步數,行走的路線按右圖所示;
如果落在梯子的底部,那麼就爬上那個梯子到達另外一個格子;
如果落到蛇的頭部,就會滑到蛇尾部所在的格子。
遊戲面板由一個Int數組組成,大小由一個常量設定finalSquare,同時用來檢測是否到達了勝利的格子。遊戲面板由26個Int數字0初始化(不是25個,因為從0到25有26個數字)
複製代碼 代碼如下:
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
第三個格子是一個梯子的底部,表示玩家可以通過梯子到達第11格,因此設定board[3]為+08,表示前進8步。同理蛇的位置設定為負數,表示後退i步。
玩家從為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!")
這個例子用到了一個非常簡單的擲骰子的方式,就是每次加1,而不是使用一個隨機數。diceRoll用來表示每次行走的步數,需要注意的是,每次執行前,++diceRoll都會先執行加1,然後再與7比較,如果等於7的話,就設定為1,因此可以看出diceRoll的變化是1,2,3,4,5,6,1……
在擲骰子之後,玩家移動diceRoll指示的步數,這時可能已經超過了finalSquare,因此需要進行if判斷,如果為true的話,執行該格子上的事件:如果是普通格子就不動,如果是梯子或者蛇就移動相應的步數,這裡只需要直接使用square += board[square]就可以了。
在while迴圈執行完畢之後,重新檢查條件square < finalSquare是否成立,繼續遊戲直到遊戲結束。
Do-while迴圈
另一種while迴圈是do-while迴圈。在這種迴圈中,迴圈體中的語句會先被執行一次,然後才開始檢測迴圈條件是否滿足,下面是do-while迴圈的一般形式:
複製代碼 代碼如下:
do {
statements
} while condition
上面的蛇與梯子的遊戲使用do-while迴圈來寫可以這樣完成。初始化語句和while迴圈的類似:
複製代碼 代碼如下:
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格,所以遊戲不會直接結束,接下來的過程就和上面的while迴圈類似了,迴圈的條件陳述式還是檢測是否已經達到最終格子。
複製代碼 代碼如下:
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!")
3、條件陳述式
通常情況下我們都需要根據不同條件來執行不同語句。比如當錯誤發生的時候,執行一些錯誤資訊的語句,告訴編程人員這個值是太大了還是太小了等等。這裡就需要用到條件陳述式。
Swift提供了兩種條件分支語句的方式,if語句和switch語句。一般if語句比較常用,但是只能檢測少量的條件情況。switch語句用於大量的條件可能發生時的條件陳述式。
if語句
在最基本的if語句中,條件陳述式只有一個,如果條件為true時,執行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華氏度(32華氏度是水的冰點,和攝氏度不一樣)低,如果低的話就會輸出一行語句。如果不低,則不會輸出。if語句塊是用大括弧包含的部分。
當條件陳述式有多種可能時,就會用到else語句,當if為false時,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."
在這種情況下,兩個分支的其中一個一定會被執行。
同樣也可以有多個分支,使用多次if和else
複製代碼 代碼如下:
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語句考察一個值的多種可能性,將它與多個case相比較,從而決定執行哪一個分支的代碼。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語句塊,除了直接比較值以外,Swift還提供了多種更加複雜的模式比對的方式來選擇語句執行的分支,這在後續的小節會繼續介紹。
在switch中,每一個case分支都會被匹配和檢測到,所有case沒有提到的情況都必須使用default關鍵詞。注意default關鍵詞必須在所有case的最後。
下面的例子用switch語句來判斷一個字元的類型:
複製代碼 代碼如下:
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"
在這個例子中,首先看這個字元是不是母音字母,再檢測是不是輔音字母。其它的情況都用default來匹配即可。
不會一直執行
跟C和Objective-C不同,Swift中的switch語句不會因為在case語句的結尾沒有break就跳轉到下一個case語句執行。switch語句只會執行匹配上的case裡的語句,然後就會直接停止。這樣可以讓switch語句更加安全,因為很多時候編程人員都會忘記寫break。
每一個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語句不會同時匹配a和A,它會直接報錯。一個case中可以有多個條件,用逗號,分隔即可:
複製代碼 代碼如下:
switch some value to consider {
case value 1,
value 2:
statements
}
範圍匹配
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中還可以直接測試元組是否符合相應的條件,_可以匹配任意值。
下面的例子是判斷(x,y)是否在矩形中,元群組類型是(Int,Int)
複製代碼 代碼如下:
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"
和C語言不同,Swift可以判斷元組是否符合條件。
數值綁定
在case匹配的同時,可以將switch語句中的值綁定給一個特定的常量或者變數,以便在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軸上,或者在其他地方。這裡用到了匹配和數值綁定。第一種情況,如果點是(x,0)模式的,將值綁定到x上,這樣在case語句中可以輸出該值。同理如果在y軸上,就輸出y的值。
Where關鍵詞
switch語句可以使用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"
每個case都因為有where而不同,第一個case就是判斷x是否與y相等,表示點在斜線y=x上。
4、控制跳躍陳述式
在Swift中控制跳躍陳述式有4種,讓編程人員更好地控制碼的流轉,包括:
continue
break
fallthrough
return
其中continue,break和fallthrough在下面詳細介紹,return語句將在函數一章介紹。
continue
continue語句告訴一個迴圈停止現在在執行的語句,開始下一次迴圈。
注意:在for-condition-increment迴圈中,increment增量語句依然執行,只是略過了一次迴圈體。
下面的例子實現的是去除一個字串中的空格和母音字母,從而組成一個字謎:
複製代碼 代碼如下:
let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput {
switch character {
case "a", "e", "i", "o", "u", " ":
continue
default:
puzzleOutput += character
}
}
println(puzzleOutput)
// prints "grtmndsthnklk"
遍曆字串的每一個字元,當遇到母音字母或者空格時就忽略,進行下一次迴圈,從而得到了最終的字謎。
break
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是不是一個數字,阿拉伯數字,漢字,拉丁文或者泰文都可以。如果匹配完成,則將possibleIntegerValue賦值。最後在通過if語句檢測是否已被賦值,並綁定到integerValue常量上,最後輸出。default語句用來迎接未能被上述case匹配的情況,但是不需要做任何事情,因此直接使用break終止即可。
fallthrough
由於Swift中的switch語句不會自動的因為沒有break而跳轉到下一個case,因此如果需要想C語言中那樣,依次執行每個case的時候,就需要用到fallthrough關鍵詞。
像下面這個例子一樣,default分支最終都會被執行:
複製代碼 代碼如下:
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和迴圈可以互相嵌套,迴圈之間也可以互相嵌套,因此在使用break或者continue的時候,需要知道到底是對哪個語句起作用。這就需要用到標籤語句。標籤語句的一般形式如下:
複製代碼 代碼如下:
label name: while condition {
statements
}
下面的例子示範了如何使用標籤語句以及嵌套的迴圈和switch。
依然採用之前的那個梯子與蛇的遊戲,第一步依然是設定初始值:
複製代碼 代碼如下:
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的嵌套來完成遊戲
複製代碼 代碼如下:
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!")
在這個代碼中,將遊戲的迴圈命名為gameLoop,然後在每一步移動格子時,判斷當前是否到達了遊戲終點,在break的時候,需要將整個遊戲迴圈終止掉,而不是終止switch,因此用到了break gameLoop。同樣的,在第二個分支中,continue gameLoop也指明了需要continue的是整個遊戲,而不是switch語句本身。