標籤:switch ios if swift
3.條件陳述式
經常會需要根據不同的情況來執行不同的代碼。你可能想要在發生錯誤的時候執行一段額外的代碼,或者當某個值變得太高或者太低的時候給他輸出出來。要實現這些需求,你可以使用條件分支。
Swift提供兩種方式來實現條件分支,也就是if語句和switch語句。一般來說If用在可能的情況比較少的簡單條件中,當遇到複雜條件有很多種可能性的時候使用switch會更好,或者要根據模式比對來判斷要執行什麼代碼的時候switch也很有用。
if語句
if的最簡單形式只有一個單獨的if條件,只有當條件為true的時候才會執行if大括弧中的代碼:
var temperatureInFahrenheit = 30if temperatureInFahrenheit <= 32 {<span style="white-space:pre"></span>println("It's very cold. Consider wearing a scarf.")}// prints "It's very cold. Consider wearing a scarf."
上面的例子檢查溫度是不是小於等於32度(水結冰的問題呢,不是攝氏度吧)。如果是,輸出語句就會執行,如果不是就不會執行,接著執行If大括弧後面的語句。
if語句還可以提供另外一個可選擇的語句塊,就是else分支,當if條件計算為false的時候將執行else的代碼,下面是包含else的代碼:
temperatureInFahrenheit = 40if temperatureInFahrenheit <= 32 {<span style="white-space:pre"></span>println("It's very cold. Consider wearing a scarf.")} else {<span style="white-space:pre"></span>println("It's not that cold. Wear a t-shirt.")}// prints "It's not that cold. Wear a t-shirt."
兩個分支總有一種會執行。因為溫度上升到40度了,已經比32度要高了,所以執行的是else分支。
你還可以將多個if連結起來,使用更多的分支:
temperatureInFahrenheit = 90if temperatureInFahrenheit <= 32 {<span style="white-space:pre"></span>println("It's very cold. Consider wearing a scarf.")} else if temperatureInFahrenheit >= 86 {<span style="white-space:pre"></span>println("It's really warm. Don't forget to wear sunscreen.")} else {<span style="white-space:pre"></span>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 = 72if temperatureInFahrenheit <= 32 {<span style="white-space:pre"></span>println("It's very cold. Consider wearing a scarf.")} else if temperatureInFahrenheit >= 86 {<span style="white-space:pre"></span>println("It's really warm. Don't forget to wear sunscreen.")}
這個例子中,溫度既不會太冷而執行if,也不會太熱而執行else if,所以什麼都沒有輸出。
switch語句
switch語句是將某個值與若干個可能的情況進行匹配。然後執行第一個匹配上的情況對應的代碼。switch語句為多種可能情況的if語句提供了另一種選擇。
switch的最簡單形式是某個簡單類型的值跟幾個同類型的值進行比較看是否相等:
switch some value to consider {<span style="white-space:pre"></span>case value 1 :<span style="white-space:pre"></span>respond to value 1<span style="white-space:pre"></span>case value 2 ,value 3 :<span style="white-space:pre"></span>respond to value 2 or 3<span style="white-space:pre"></span>default:<span style="white-space:pre"></span>otherwise, do something else}
switch語句由多個可能匹配的case組成。除了比較特殊的值,swift還提供幾種方式來給每種情況指定更複雜的匹配模式。這些內容在本章後面會介紹的。
switch的各個case都是獨立的執行分支,switch語句決定哪條分支被執行。switch語句必須是完善的,也就是說所有要考慮的情況都必須跟一個case匹配。最好是給每一種可能的情況都提供一個case,你還可以定義一個預設的情況,如果前面所有情況都沒匹配上就會執行這個預設的分支,預設分支使用default關鍵字,而且必須出來再所有的case之後。
下面例子使用switch語句來檢查一個單獨的小寫字母someCharacter:
let someCharacter: Character = "e"switch someCharacter {<span style="white-space:pre"></span>case "a", "e", "i", "o", "u":<span style="white-space:pre"></span>println("\(someCharacter) is a vowel")<span style="white-space:pre"></span>case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":<span style="white-space:pre"></span>println("\(someCharacter) is a consonant")<span style="white-space:pre"></span>default:<span style="white-space:pre"></span>println("\(someCharacter) is not a vowel or a consonant")}// prints "e is a vowel"
上面switch的第一個case匹配任意的母音字母,第二個case匹配任意的輔音字母。要寫出其他全部的字母有點兒不太可能,所以最後使用了default來匹配除了母音和輔音字母以外的任一字元。加上這個default就保證了switch語句的完備性。
沒有隱式順序執行
與C或者OC裡的switch對比,swift的switch語句預設不會再去一個個匹配已經匹配到的case後面的case,當程式匹配到一個case,執行case的代碼塊,switch就結束了,代碼塊末尾不需要使用break語句。這使得switch語句比C更安全簡單,避免了因為忘記寫break造成程式執行了好幾個case。
NOTE 如果你覺得沒有break看著彆扭,你也可以加上。
每個case都必須包含至少一條語句,下面這樣寫就是錯的,因為第一個case是空的:
let anotherCharacter: Character = "a"switch anotherCharacter {<span style="white-space:pre"></span>case "a":<span style="white-space:pre"></span>case "A":<span style="white-space:pre"></span>println("The letter A")<span style="white-space:pre"></span>default:<span style="white-space:pre"></span>println("Not the letter A")}// this will report a compile-time error
這可跟C語言不一樣了,switch語句不會同時匹配"a"和“A”,而是給case "a":報編譯錯誤“does not contain any executeable statements(不包含任何可執行檔語句)”。這樣避免了不小心從一個case再執行到另一個case,使代碼又安全又意圖清晰。
如果要一個case匹配多種情況,就使用逗號分隔,而且很長的可以分行寫:
switch some value to consider {<span style="white-space:pre"></span>case value 1 ,<span style="white-space:pre"></span> value 2 :<span style="white-space:pre"></span>statements}
NOTE 如果要讓匹配命中後面的case順序執行,可以使用fallthrough關鍵字,後面也會專門介紹。
範圍匹配
switch的case匹配的值也可以使用範圍。下面的例子使用數字範圍來顯示數位自然語言形式
let count = 3_000_000_000_000let countedThings = "stars in the Milky Way"var naturalCount: Stringswitch 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.”
元組
你可以在switch語句中使用元組來對多個值做判斷。元組中的元素各自都能被單個值或者某個範圍來比較。或者你也可以使用底線來讓他匹配任意值。
下面舉例使用(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")}// prints "(1, 1) is inside the box”
switch語句判斷點是不是在原點(0,0),或者紅色的x軸上,或者橘黃色的y軸上,或者是在藍色地區內,或者是來藍色地區以外。
和C不一樣的是,Swift可以多個case使用重複的值,事實上(0,0)是可以匹配上面四個case的。但是就算多個case都能匹配,第一個被匹配的才會被執行。所以點(0,0)會匹配第一個(0,0)的情況,其他的就被忽略了。
值綁定
case可以將他匹配的值綁定到臨時常量或者變數中,然後在case的代碼塊裡使用。這就是值綁定,因為值只是在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中臨時擷取x或者y或者x和y的值。第一個case,caes (let x,0)匹配任何y座標為0的點,並且把這個點得x座標賦值給臨時常量x,第二個case, case (0, let y)匹配所有x座標為0的點,然後將這個點得y座標賦值給臨時常量y。
只要是臨時常量被生命了,那在這個case的代碼塊裡就可以使用這個常量。對於這個例子來說,他們被用來輸出座標的值。
注意這個switch是沒有default的,因為最後一個case let(x, y)使用了兩個預留位置,那他就會匹配所有的點,所以也就不用再加default了,這個switch已經很完整了。
上面的例子中,x和y被定義成常量,那是因為我們在case裡面沒有必要去修改座標的值。如果你想聲明成變數就用var關鍵字,如果是這樣的話,臨時變數應該是已經被建立並且用合適的值初始化了得。任何對於該變數的修改都只能在這個case裡面有效。
where
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判斷點是否在綠色直線(x=y)上,或者在紫色直線(x = -y)上,或者其他地方。
三個case都聲明了常量x和y,暫時儲存了點得座標值。這些常量也被用來作為case中where語句的一部分,來建立一個動態過濾器。這個case只有噹噹前匹配的點滿足where的運算式計算結果為true的時候才會被匹配。
和前一個例子一樣,最後一個case會匹配剩下所有的點,所以這個switch也沒有加default。
4.控制轉移語句
控制轉移語句可以將控制從一段代碼轉移到另一段代碼,以此來改變代碼的執行順序。Swift有四種控制轉移語句
continue
break
fallthrough
return
下面介紹前三種,最後一個return會在函數的章節裡再介紹。
continue
continue語句告訴迴圈停止當前的迴圈,然後開始下一次迴圈。也就是說“本次迴圈任務已經執行完了”,而不會完全跳出迴圈。
NOTE 在for-condition-increment型的迴圈中,如果使用了continue語句,遞增語句還是會被執行的。迴圈本身的順序還是正常的,只是迴圈體被跳過了而已。
下面的例子將小寫字串中的母音字母和空格都去掉了,最後產生一個沒什麼含義的短語
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”
上面的代碼只要碰到母音或者空格就使用continue,當前的迴圈就被終止,開始下一次迴圈了。這樣可以讓switch只匹配並忽略母音字母及空格,而不是要求代碼塊匹配所有需要輸出的字元。
break
break語句終止當前的整個控制流程。break可以用來switch或者迴圈當中來提前退出整個switch或者迴圈。
在迴圈中break
如果在迴圈語句中使用break,整個迴圈立馬就被終止了,轉移執行迴圈的大括弧後面的代碼。當前迴圈不會被執行了,下一次以及以後的迴圈也不會被執行了。
在switch中break
如果在switch中使用break,整個switch也立即被終止,開始執行switch以後的代碼了。可以使用break來匹配並且忽略一種或多種情況。因為swift的switch是完備的,並且不允許空分支,有時為了故意匹配並且忽略某些情況,來使意圖更明顯。那麼就把break作為想要忽略的case代碼塊的唯一一句代碼。當那個case被匹配到了,整個switch語句立馬就被終止了。
NOTE 如果case只有注釋,編譯器會報編譯錯誤。注釋不是語句,也不會是case被忽略。如果想讓case被忽略掉就用break。
下面的例子通過switch來判斷字元是否代表下面四種語言之一。為了簡潔這邊在一個case中匹配多個值:
let numberSymbol: Character = "三" // Simplified Chinese for the number 3var possibleIntegerValue: Int?switch numberSymbol {case "1", "?", "一", "?": possibleIntegerValue = 1case "2", "?", "二", "?": possibleIntegerValue = 2case "3", "?", "三", "?": possibleIntegerValue = 3case "4", "?", "四", "?": possibleIntegerValue = 4default: 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,2,3,4。如果發現某個匹配的,那個case就給可選變數possibleIntegerValue賦值。
當switch執行完的時候,使用可選值綁定來判斷possibleIntegerValue是否有值,因為這個可選變數隱式使用nil初始化的,所以如果只有當某個case給他賦值的時候下面的if才會通過。
要列出上面例子中所有可能的字元就不太實際了,所以用了個deault來匹配剩下的所有字元。這個分支不需要做任何操作,所以就唯寫了一個break語句。只要最後的default被匹配,整個switch就被break終止了,開始執行下面的if了。
fallthrough
swift的switch語句如果匹配到了某個case是不會再繼續向下執行的,執行完這個case的代碼整個switch也就結束了。但是在C裡面是要求在case的結尾加一個break才能有這樣的效果。避免了預設掛穿執行意味著swift的switch語句比C要更加簡潔並且可預料,也避免了因為忘記寫break而多執行了其他的case。
如果你確實需要像C那樣貫穿的特性,你可以通過使用fallthrough關鍵字來滿足。下面的自理使用fallthrough來建立數位文本描述:
let integerToDescribe = 5var description = "The number \(integerToDescribe) is"switch integerToDescribe {case 2, 3, 5, 7, 11, 13, 17, 19: description += " a prime number, and also" fallthroughdefault: description += " an integer."}println(description)// prints "The number 5 is a prime number, and also an integer.”
例子中聲明了一個字串變數description並且給他賦初始值。然後使用switch來測試integerToDescribe的值。如果是第一個case中的某個素數,就在description後面加一段文字說明這個數字是素數。然後使用fallthrough關鍵字來讓程式繼續直接進入下一個case default,default case給description添加了其他的描述資訊,然後整個switch才結束。
如果integerToDescribe沒有在第一個case當中,第一個case也就不會被匹配。也沒有其他特殊的分支了,所以就被default匹配了。
當switch語句執行完以後,數位描述被使用println函數輸出。這個例子中數字5被正確的認出是素數。
NOTE fallthrough不會檢查下一個將要匹配的分支的條件,他只是簡單地讓程式直接進入下一個分支,就像C標準的switch語句一樣。
標籤語句
你可以通過在switch或者迴圈當中嵌套其他的switch或者迴圈來實現複雜的控制流程結構。switch和迴圈都可以使用break語句來提前結束程式。但是有時候需要指定你想跳出的是哪個迴圈或者switch。比如說你嵌套了好幾個迴圈,指明break應該跳出哪個迴圈就很需要了。
要想達到這個目的,你可以給迴圈或者switch設定一個標籤,然後再break或者continue後面加上這個標籤,就能讓break或者continue影響這個迴圈或者switch了。
標籤語句是通過在語句本身前面加上標籤名稱再加上冒號。下面有個給while語句使用標籤的例子,這個規則對於所有的迴圈和switch都是通用的:
label name: while condition { statements}
下面的例子使用帶標籤的break和continue語句重新實現上面寫過的蛇與梯子的遊戲。這次遊戲規則要加一條:若且唯若你身在第25格的時候才算過關
如果某次擲色子然後跳到25格以外了,那就重新擲色子,一直到你站在25格上。遊戲棋盤跟前面的一樣:
finalSquare,borad,square以及diceRoll的初始化方式跟前面也是一樣的:
let finalSquare = 25var board = Int[](count: finalSquare + 1, repeatedValue: 0)board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08var square = 0var 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條件陳述式判斷是否需要進行下一次迴圈。
NOTE 如果上面的break語句不適用gameLoop標籤,那break只會跳出switch而不會終止整個while迴圈,使用gameLoop標籤可以清楚的指明需要結束哪個語句。
還要注意的時當在gameLoop迴圈裡要使用continue進行下一次迴圈的時候可以不用gameLoop標籤的。因為這個遊戲裡只有一個迴圈,所以沒有必要指明continue對誰起作用。
但是你要用也無妨。這樣可以使程式邏輯清晰易於理解。