Swfit - 控制流
前言
1 - Swift 提供了多种流程控制结构:包括可以多次执行任务的 while 循环;基于特定条件选择执行不同代码分支的 if、guard 和 switch!注: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) }
/* to 遍历结果:开区间 * 0 * 15 * 30 * 45 */
/* through 遍历结果:闭区间 * 0 * 15 * 30 * 45
* 60 */
while | repeat ... while
1 - Swift 提供两种 while 循环形式:while 和 repeat ... while(同 do ... while)
2 - 下面制作一款游戏
① 需求如下
游戏盘面包括 25 个方格,游戏目标是达到或者超过第 25 个方格则结束!
在每一轮中,你通过掷一个六面体骰子来确定你移动方块的步数,移动的路线由下图中横线所示:如果在某轮结束,你移动到了梯子的底部,可以顺着梯子爬上去;如果在某轮结束,你移动到了蛇的头部,你会顺着蛇的身体滑下去
② 思路
游戏盘面可以使用一个 Int 数组来表达。数组的长度由一个 finalSquare 常量储存,用来初始化数组和检测最终胜利条件
梯子底部的方格是一个正值,使你可以向上移动;蛇头处的方格是一个负值,会让你向下移动
1 let finalSquare = 25 2 var board = [Int](repeating: 0, count: finalSquare + 1)// 游戏盘面由 26 个 0 的初始化 3 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 // 记录掷甩子 9 var diceRoll: Int = 0 10 // 记录移动步数 11 var square:Int = 0 12 13 14 // 方式一:使用 while 15 // 游戏没有结束则一直跑循环 16 while square < finalSquare { 17 18 // 掷骰子 19 diceRoll = Int(arc4random() % 6)+1; 20 // print(diceRoll) 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 34 // 条件初始化 35 diceRoll = 0 36 square = 0 37 38 // 方式二:Repeat-While 39 repeat { 40 41 // 顺着梯子爬上去或者顺着蛇滑下去 42 square += board[square] 43 // 掷骰子 44 diceRoll = Int(arc4random() % 6)+1; 45 // print(diceRoll) 46 // 根据点数移动 47 square += diceRoll 48 } while square < finalSquare 49 print("Game over!")
条件语句
1 - if、if ... else、if ... else if ....
2 - switch
① 为了匹配某些更特定的值,Swift 提供了几种方法来进行更复杂的模式匹配
let someCharacter: Character = "z" switch someCharacter { case "a": print("The first letter of the alphabet") case "z": // 在 Swift 中不需要在 case 分支中显式地使用 break 语句 print("The last letter of the alphabet") // 执行此分支 // 虽然 break 不是必须的,但你依然可以使用 break // 每一个 case 分支都必须包含至少一条语句,如下使用方式报错 // case "k": // 无效:这个分支下面没有语句 // 为了让单个 case 同时匹配多个值,可以将这些值组合成一个复合匹配,用逗号分开 case "m","n": print("The letter A") default: print("Some other character") }
② 区间匹配
// 区间匹配 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" } print(naturalCount) // dozens of
③ 使用元组在同一个 switch 语句中测试多个值
1 let somePoint = (1, 0) 2 switch somePoint { 3 4 case (0, 0): 5 print("\(somePoint) is at the origin") 6 // 可使用下划线 _ 来匹配所有可能的值 7 case (_, 0): 8 print("\(somePoint) is on the x-axis") // 执行该分支,标记2 9 case (0, _): 10 print("\(somePoint) is on the y-axis") 11 case (-2...2, -2...2),(1,3): 12 print("\(somePoint) is inside the box") // 此分支不再执行,被标记2截断 13 default: 14 print("\(somePoint) is outside of the box") 15 }
④ 值绑定:case 分支允许将匹配的值声明为临时常量或变量,并且在 case 分支体内使用
let anotherPoint = (2, 0) switch anotherPoint { // 常量 x 和 y 的占位符,用于临时获取元组 anotherPoint 的值 // 一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里使用 case let (x, y): print("at (\(x), \(y))") // 执行该分支 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)") }
⑤ 使用 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 - 改变代码执行顺序,可以实现代码的跳转。有五种控制转移语句
① continue
② break
③ 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.
④ return
⑤ throw
标签名
1 - 可以使用标签来标记一个循环体或者条件语句。比如我们学习 while 时写了一个小游戏,现在改变规则:为了获胜,你必须刚好落在第 25 个方块中,就是说如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中
1 // 条件初始化 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 // 步数 7 var square = 0 8 // 骰子 9 var diceRoll = 0 10 11 // 标签名 gameLoop 12 gameLoop: while square != finalSquare { 13 14 diceRoll = Int(arc4random() % 6)+1; 15 switch square + diceRoll { 16 case finalSquare: 17 // 骰子数刚好使玩家移动到最终的方格里,游戏结束! 18 break gameLoop // 中断 while 循环 19 // 如果 break 后没有使用 gameLoop 标签,那么它中断的是 switch 语句而不是 while 循环 20 21 // newSquare 等价于 square + diceRoll 22 case let newSquare where newSquare > finalSquare: 23 // 玩家的移动超出最后的方格,那么需要重新掷骰子 24 continue gameLoop 25 26 default: 27 // 合法移动,做正常的处理 28 square += diceRoll 29 square += board[square] 30 } 31 } 32 print("Game over!")
guard
1 - guard 的执行取决于一个表达式的布尔值,不同于 if 语句,一个 guard 语句总是有一个 else 从句。相比于可以实现同样功能的 if 语句,按需使用 guard 语句会提升我们代码的可读性。注: else 分支必须转移控制以退出 guard 语句出现的代码段,如使用 return、break、continue 或者 throw
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"]) // Hello John! // I hope the weather is nice near you. greet(person: ["name": "Jane", "location": "Cupertino"]) // Hello Jane! // 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 }