Swfit - 控制流
前言
1 - Swift 提供了多种流程控制结构:包括可以多次执行任务的 while 循环;基于特定条件选择执行不同代码分支的 if、guard 和 switch
2 - Swift 中的 switch 语句比 C语言 加强大
① 它可以匹配很多不同的模式,包括范围匹配、元组和特定类型匹配
② 还可以声明为临时常量或变量在 case 作用域内使用
③ 也可以配合 where 来描述更复杂的匹配条件
For-In
1 - 遍历数组、字典、范围区间
// 数组 let names = ["Anna", "Alex", "Brian", "Jack"] for name in names { print("Hello, \(name)!") } // 字典 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)") }
2 - 如果不需要区间序列内每一项的值,可以使用下划线 _ 替代变量名来忽略这个值。下划线符号能够忽略当前值,并且不提供循环遍历时对值的访问
let base = 3 let power = 4 var answer = 1 // 计算 base 这个数的 power 次幂 for _ in 1...power { answer = answer * base } print(answer)// 81
3 - stride(from:to:by:) | stride(from:through:by:) 两个函数可跳过不需要的标记,前者是开区间,后者是闭区间
let minutes = 60 let minuteInterval = 15 // 15 为间隔遍历 for tickMark in stride(from: 0, to: minutes, by: minuteInterval) { print(tickMark) } /* 遍历结果 * 0 * 15 * 30 * 45 */
While | repeat ... while
1 - Swift 提供两种 while 循环形式:while 和 repeat ... while(同 do ... while)
2 - 代码示例:制作一款游戏
需求如下
游戏盘面包括 25 个方格,游戏目标是达到或者超过第 25 个方格
每一轮,你通过掷一个六面体骰子来确定你移动方块的步数,移动的路线由上图中横向的虚线所示
如果在某轮结束,你移动到了梯子的底部,可以顺着梯子爬上去
如果在某轮结束,你移动到了蛇的头部,你会顺着蛇的身体滑下去
② 思路
游戏盘面可以使用一个 Int 数组来表达。数组的长度由一个 finalSquare 常量储存,用来初始化数组和检测最终胜利条件
梯子底部的方格是一个正值,使你可以向上移动;蛇头处的方格是一个负值,会让你向下移动
1 // 方式一:使用 while 2 let finalSquare = 25 3 var board = [Int](repeating: 0, count: finalSquare + 1)// 游戏盘面由 26 个 Int 0 值初始化 4 // 前进、后退 5 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 6 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 7 8 var diceRoll = 0// 掷甩子 9 var square = 0 // 移动步数 10 11 // 没达到顶格 12 while square < finalSquare { 13 // 掷骰子 14 diceRoll += 1 15 16 // 当 diceRoll 的值等于 7 时,就超过了骰子的最大值,重置为 1 17 if diceRoll == 7{ 18 19 diceRoll = 1 20 } 21 // 根据点数移动 22 square = square + diceRoll 23 24 // 如果玩家还在棋盘上 25 if square < board.count { 26 // 顺着梯子爬上去或者顺着蛇滑下去 27 square += board[square] 28 } 29 } 30 print("Game over!") 31 32 33 // 方式二:使用 Repeat-While 34 let finalSquare02 = 25 35 var board02 = [Int](repeating: 0, count: finalSquare02 + 1) 36 board02[03] = +08; board02[06] = +11; board02[09] = +09; board02[10] = +02 37 board02[14] = -10; board02[19] = -11; board02[22] = -02; board02[24] = -08 38 var square02 = 0 39 var diceRoll02 = 0 40 repeat { 41 // 顺着梯子爬上去或者顺着蛇滑下去 42 square02 += board02[square] 43 // 掷骰子 44 diceRoll02 += 1 45 if diceRoll02 == 7 { diceRoll02 = 1 } 46 // 根据点数移动 47 square02 += diceRoll02 48 } while square02 < finalSquare02 49 print("Game over!")
条件语句
1 - if、if ... else、if ... else if ....
2 - switch
① 为了匹配某些更特定的值,Swift 提供了几种方法来进行更复杂的模式匹配
1 let someCharacter: Character = "z" 2 switch someCharacter { 3 4 case "a": 5 print("The first letter of the alphabet") 6 7 case "z": 8 // 在 Swift 中不需要在 case 分支中显式地使用 break 语句 9 // 当匹配的 case 分支中的代码执行完毕后,程序会终止该分支 10 print("The last letter of the alphabet") // 执行此分支 11 // 虽然 break 不是必须的,但你依然可以使用 12 break 13 14 // 每一个 case 分支都必须包含至少一条语句,如下使用方式报错 15 // case "k": // 无效:这个分支下面没有语句 16 17 // 为了让单个 case 同时匹配多个值,可以将这些值组合成一个复合匹配,用逗号分开 18 case "m","n": 19 print("The letter A") 20 default: 21 print("Some other character") 22 }
② 区间匹配
// 区间匹配 let approximateCount = 62 let 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" }
③ 使用元组在同一个 switch 语句中测试多个值
let somePoint = (1, 1) switch somePoint { case (0, 0): print("\(somePoint) is at the origin") // 使用下划线 _ 来匹配所有可能的值 case (_, 0): print("\(somePoint) is on the x-axis") case (0, _): print("\(somePoint) is on the y-axis") case (-2...2, -2...2): print("\(somePoint) is inside the box") // 执行该分支 default: print("\(somePoint) is outside of the box") }
④ 值绑定:case 分支允许将匹配的值声明为临时常量或变量,并且在 case 分支体内使用
let anotherPoint = (2, 0) switch anotherPoint { // 常量 x 和 y 的占位符,用于临时获取元组 anotherPoint 的值 // (let x, 0) 将匹配一个纵坐标为 0 的点,并把这个点的横坐标赋给临时的常量 x。以下同理 // 一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里使用 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("at (\(x), \(y))") }
⑤ 使用 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") }
⑥ 复合型 Cases
// 复合型 Cases // 当多个条件可以使用同一种方法来处理时,可以将这几种可能放在同一个 case 后面,并且用逗号隔开 let someCharacter: Character = "e" switch someCharacter { case "a", "e", "i", "o", "u": print("\(someCharacter) is a vowel")// 执行该分支 case "b", "c", "d", "f", "g": 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") }
控制转移语句
1 - 改变代码执行顺序,可以实现代码的跳转
2 - 有五种控制转移语句
① continue :本次循环我已经执行完
② break:束整个控制流的执行
③ fallthrough :贯穿
④ return
⑤ throw
// 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." } // 两个分支均会执行 print(description) // The number 5 is a prime number, and also an integer.
标签名
1 - 可以使用标签(statement label)来标记一个循环体或者条件语句
2 - 我们学习 while 时写了一个小游戏,现在改变规则:为了获胜,你必须刚好落在第 25 个方块中,就是说如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中
1 // finalSquare、board、square 和 diceRoll 值被和之前一样的方式初始化 2 let finalSquare = 25 3 var board = [Int](repeating: 0, count: finalSquare + 1) 4 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 5 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 6 var square = 0 7 var diceRoll = 0 8 9 // 标签名 gameLoop 10 gameLoop: while square != finalSquare { 11 12 diceRoll += 1 13 if diceRoll == 7 { diceRoll = 1 } 14 15 switch square + diceRoll { 16 case finalSquare: 17 // 骰子数刚好使玩家移动到最终的方格里,游戏结束。 18 break gameLoop // 如果 break 语句没有使用 gameLoop 标签,那么它将会中断 switch 语句而不是 while 循环 19 20 case let newSquare where newSquare > finalSquare: 21 // 骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子 22 continue gameLoop 23 24 default: 25 // 合法移动,做正常的处理 26 square += diceRoll 27 square += board[square] 28 } 29 } 30 print("Game over!")
guard
1 - guard 的执行取决于一个表达式的布尔值,不同于 if 语句,一个 guard 语句总是有一个 else 从句。相比于可以实现同样功能的 if 语句,按需使用 guard 语句会提升我们代码的可读性。值得注意的是 else 分支必须转移控制以退出 guard 语句出现的代码段,如使用 return、break、continue 或者 throw
1 func greet(person: [String: String]) { 2 guard let name = person["name"] else { 3 return 4 } 5 6 print("Hello \(name)!") 7 8 guard let location = person["location"] else { 9 print("I hope the weather is nice near you.") 10 return 11 } 12 13 print("I hope the weather is nice in \(location).") 14 } 15 16 greet(person: ["name": "John"]) 17 // Hello John! 18 // I hope the weather is nice near you. 19 20 greet(person: ["name": "Jane", "location": "Cupertino"]) 21 // Hello Jane! 22 // I hope the weather is nice in Cupertino.
检测 API 可用性
1 - Swift 支持检查 API 可用
// 最后一个参数,* 是必须的,用于指定在所有其它平台中 if #available(iOS 10, macOS 10.12, *) { // 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API } else { // 使用先前版本的 iOS 和 macOS 的 API }