Swift--控制流 (Control Flow)

weixin_34253539發表於2017-10-25

文章摘要

  • For-In 迴圈
  • While
  • if
  • Switch
  • 控制轉移語句

注意點

  • repeat-while 迴圈
  • 在 Swift 中,當匹配的 case 分支中的程式碼執行完畢後,程式會終止switch語句,而不會繼續執行下一個 case 分支。
  • 區間匹配
  • 值繫結 (Value Bindings)
  • where
  • fallthrough
  • 帶標籤的語句
  • guard else
  • #available(iOS 10, macOS 10.12, *)

For-In 迴圈

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
    print("Hello, \(name)!")
}
//可以在 for-in 迴圈中使用顯式的常量名稱來解讀 (key, value) 元組
let numberOfLegs = ["spider": 8, "ant": 6, "cat":4]
for (animalName, legCount) in numberOfLegs {
    print("\(animalName)s have \(legCount) legs")
}
//迴圈還可以使用數字範圍
for index in 1...5 {
    print("\(index) times 5 is \(index * 5)")
}
//可以使用下劃線(_)替代變數名來忽略這個值
let base = 3
let power = 10
var answer = 10
for _ in 1...power {
    answer *= base
}
//使用半開區間運算子(..<)來表示一個左閉右開的區間
let minutes = 60
for tickMark in 0..<minutes {
    // 每1分鐘呈現一個刻度線(60次
    print("tickMark: \(tickMark)")
}
//可以每5分鐘作為一個刻度。使用 stride(from:to:by:) 函式跳過不需要的標記。
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: 5) {
    // 每5分鐘呈現一個刻度線 (0, 5, 10, 15 ... 45, 50, 55)
    print("tickMark: \(tickMark)")
}
//可以在閉區間使用 stride(from:through:by:) 起到同樣作用:
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
     // 每3小時呈現一個刻度線 (3, 6, 9, 12)
    print("tickMark: \(tickMark)")
}

While

while迴圈會一直執行一段語句直到條件變成false。
Swift 提供兩種while
迴圈形式:

  • while 迴圈,每次在迴圈開始時計算條件是否符合;
  • repeat-while 迴圈,每次在迴圈結束時計算條件是否符合。

While

//while condition {
//    statements
//}

蛇和梯子(也叫做滑道和梯子)的小遊戲

let finalSquare = 25
var board = [Int](repeating:0, count:finalSquare + 1)

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 square < finalSquare {
    //投骰子
    diceRoll += 1
    if diceRoll == 7 {diceRoll = 1}
    //根據點數移動
    square += diceRoll
    if square < board.count {
        //如果玩家還在棋盤上,順著梯子爬上去或者順著蛇滑下去
        square += board[square]
    }
}
print("Game over!")

Repeat-While

while迴圈的另外一種形式是repeat-while,它和while的區別是在判斷迴圈條件之前,先執行一次迴圈的程式碼塊。然後重複迴圈直到條件為false。
格式:

//repeat {
//    statements
//} while condition

使用repeat-while迴圈來替代while迴圈。

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

repeat {
    //順著梯子爬上去或者順著蛇滑下去
    square += board[square]
    //投骰子
    diceRoll += 1
    if diceRoll == 7 {
        diceRoll = 1
    }
    //根據點數移動
    square += diceRoll
} while square < finalSquare
print("Game over!")

條件語句

if

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

執行 else 語句

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

更多分支

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

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

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
//}
let someCharacter: Character = "z"
switch someCharacter {
case "a":
    print("The first letter of the alphabet")
case "z":
    print("The last letter of the alphabet")
default:
    print("Some other character")
}

與 C 和 Objective-C 中的switch語句不同,在 Swift 中,當匹配的 case 分支中的程式碼執行完畢後,程式會終止switch語句,而不會繼續執行下一個 case 分支。

//每一個 case 分支都必須包含至少一條語句。像下面這樣書寫程式碼是無效的,因為第一個 case 分支是空的:
let anotherCharacter: Character = "a"
switch anotherCharacter {
//case "a"://無效,這個分支沒有下面沒有語句 每一個 case 分支都必須包含至少一條語句 error
case "A":
    print("The letter A")
default:
    print("Not the letter A")
}

//為了讓單個case同時匹配a和A,可以將這個兩個值組合成一個複合匹配,並且用逗號分開:
switch anotherCharacter {
case "a", "A":
    print("The letter A")
default:
    print("Not the letter A")
}

如果想要顯式貫穿case分支,請使用fallthrough語句,詳情請參考貫穿

區間匹配

//case 分支的模式也可以是一個值的區間。
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}

//元組
//元組中的元素可以是值,也可以是區間。另外,使用下劃線(_)來匹配所有可能的值。
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("(0, 0) is at the origin")
    
case (_,0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0,_):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}

值繫結 (Value Bindings)

//case 分支允許將匹配的值繫結到一個臨時的常量或變數,並且在case分支體內使用 —— 這種行為被稱為值繫結(value binding),因為匹配的值在case分支體內,與臨時的常量或變數繫結。

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

where

//case 分支的模式可以使用where語句來判斷額外的條件。

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

複合匹配

//可以將這幾種可能放在同一個case後面,並且用逗號隔開

switch someCharacter {
case "a", "e", "i", "o", "u":
    print("\(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":
    print("\(someCharacter) is a consonant")
default:
    print("\(someCharacter) is not a vowel or a consonant")
}

let stillAnotherPoint = (9, 0)
switch stillAnotherPoint {
case (let distance, 0), (0, let distance):
    print("On an axis, \(distance) from the origin")
default:
    print("Not on an axis")
}

控制轉移語句

  • continue 語句告訴一個迴圈體立刻停止本次迴圈,重新開始下次迴圈。就好像在說“本次迴圈我已經執行完了”,但是並不會離開整個迴圈體。
  • break語句會立刻結束整個控制流的執行。當你想要更早的結束一個switch程式碼塊或者一個迴圈體時,你都可以使用break語句
  • fallthrough 如果你確實需要 C 風格的貫穿的特性,你可以在每個需要該特性的 case 分支中使用fallthrough關鍵字。
  • return
  • throw

continue

continue語句告訴一個迴圈體立刻停止本次迴圈,重新開始下次迴圈。

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput.characters {
    switch character {
    case "a", "e", "i", "o", "u", " ":
        continue
    default:
        puzzleOutput.append(character)
    }
}
print(puzzleOutput)

break

break語句會立刻結束整個控制流的執行。break 可以在 switch 或迴圈語句中使用,用來提前結束switch或迴圈語句。

let numberSymbol: Character = "三"  // 簡體中文裡的數字 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 {
    print("The integer value of \(numberSymbol) is \(integerValue).")
} else {
    print("An integer value could not be found for \(numberSymbol).")
}

fallthrough

在 Swift 裡,switch語句不會從上一個 case 分支跳轉到下一個 case 分支中。相反,只要第一個匹配到的 case 分支完成了它需要執行的語句,整個switch程式碼塊完成了它的執行。

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."
}
print(description)

帶標籤的語句

可以使用標籤(statement label)來標記一個迴圈體或者條件語句,對於一個條件語句,你可以使用break加標籤的方式,來結束這個被標記的語句。

//同樣的規則適用於所有的迴圈體和條件語句。
//label name:while condition {statements}

board = [Int](repeating:0, count:finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
square = 0
diceRoll = 0
//label
gameLoop: while square != finalSquare {
    diceRoll += 1
    if diceRoll == 7 {
        diceRoll = 1
    }
    switch square + diceRoll {
    case finalSquare:
        //骰子數剛好使玩家移動到最終的方格里,遊戲結束
        break gameLoop //沒有gameLoop將中斷switch而不是while
    case let newSquare where newSquare > finalSquare:
        //骰子數將會使玩家的移動超出最後的方格,那麼這種移動是不合法的,玩家需要重新投骰子
        continue gameLoop //只有一個迴圈,可省略gameLoop
    default:
        //合法移動,做正常的處理
        square += diceRoll
        square += board[square]
    }
}
print("Gamen  over")

提前退出

//像if語句一樣,guard的執行取決於一個表示式的布林值。我們可以使用guard語句來要求條件必須為真時,以執行guard語句後的程式碼。不同於if語句,一個guard語句總是有一個else從句,如果條件不為真則執行else從句中的程式碼

func greet(person:[String: String]) {
    guard let name = person["name"] else {
        return
    }
    print("Hello \(name)")
    guard let location = person["location"] else {
        print("I hope the weather is nice near you.")
        return
    }
    print("I hope the weather is nice in \(location)")
}

greet(person: ["name" : "John"])
greet(person: ["name": "Jane", "location": "Cupertino"])

檢測API可用性

//我們在if或guard語句中使用可用性條件(availability condition)去有條件的執行一段程式碼,來在執行時判斷呼叫的API是否可用。編譯器使用從可用性條件語句中獲取的資訊去驗證,在這個程式碼塊中呼叫的 API 是否可用。

//平臺名字可以是iOS,macOS,watchOS和tvOS
//if #available(platform name version, ..., *) {
//    APIs 可用,語句將執行
//} else {
//    APIs 不可用,語句將不執行
//}

if #available(iOS 10, macOS 10.12, *) {
    // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
    // 使用先前版本的 iOS 和 macOS 的 API
}

相關文章