The Swift Programming Language-官方教程精译Swift(6)控制流--Control Flow
Swift提供了类似C语言的流程控制结构,包括可以多次执行任务的for和while循环,基于特定条件选择执行不同代码分支的if和switch语句,还有控制流程跳转到其他代码的break和continue语句。
1 for index in 1...5 { 2 println("\(index) times 5 is \(index * 5)") 3 } 4 // 1 times 5 is 5 5 // 2 times 5 is 10 6 // 3 times 5 is 15 7 // 4 times 5 is 20 8 // 5 times 5 is 25
1 let base = 3 2 let power = 10 3 var answer = 1 4 for _ in 1...power { 5 answer *= base 6 } 7 println("\(base) to the power of \(power) is \(answer)") 8 // prints "3 to the power of 10 is 59049
这个例子计算 base 这个数的 power 次幂(本例中,是 3 的 10 次幂),从 1 (3 的 0 次幂)开始做 3 的乘法, 进行 10 次,使用 0 到 9 的半闭区间循环。这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号 _ (替代循环中的变量)能够忽略具体的值,并且不提供循环遍历时对值的访问。
1 let names = ["Anna", "Alex", "Brian", "Jack"] 2 for name in names { 3 println("Hello, \(name)!") 4 } 5 // Hello, Anna! 6 // Hello, Alex! 7 // Hello, Brian! 8 // Hello, Jack!
你也可以通过遍历一个字典来访问它的键值对(key-value pairs)。遍历字典时,字典的每项元素会以 (key, value)元组的形式返回,你可以在 for-in 循环中使用显式的常量名称来解读 (key, value)元组。下面的例子中,字典的键(key)解读为常量 animalName ,字典的值会被解读为常量 legCount :
1 let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] 2 for (animalName, legCount) in numberOfLegs { 3 println("\(animalName)s have \(legCount) legs") 4 } 5 // spiders have 8 legs 6 // ants have 6 legs 7 // cats have 4 legs
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。更多数组和字典相关内容,查看集合类型章节。
1 for character in "Hello" { 2 println(character) 3 } 4 // H 5 // e 6 // l 7 // l 8 // o
1 for var index = 0; index < 3; ++index { 2 println("index is \(index)") 3 } 4 // index is 0 5 // index is 1 6 // index is 2
1 for initialization; condition; increment { 2 statements 3 }
1 initialization 2 while condition { 3 statements 4 increment 5 }
1 var index: Int 2 for index = 0; index < 3; ++index { 3 println("index is \(index)") 4 } 5 // index is 0 6 // index is 1 7 // index is 2 8 println("The loop statements were executed \(index) times") 9 // prints "The loop statements were executed 3 times
1 while condition { 2 statements 3 }
1 let finalSquare = 25 2 var board = Int[](count: finalSquare + 1, repeatedValue: 0)
一些方块被设置成有蛇或者梯子的指定值。梯子底部的方块是一个正值,是你可以向上移动,蛇头处的方块是一个负值,会让你向下移动:
1 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 2 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
1 var square = 0 2 var diceRoll = 0 3 while square < finalSquare { 4 // roll the dice 5 if ++diceRoll == 7 { diceRoll = 1 } 6 // move by the rolled amount 7 square += diceRoll 8 if square < board.count { 9 // if we're still on the board, move up or down for a snake or a ladder 10 square += board[square] 11 } 12 } 13 println("Game over!")
1 do { 2 statements 3 } while condition
1 let finalSquare = 25 2 var board = Int[](count: finalSquare + 1, repeatedValue: 0) 3 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 4 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 5 var square = 0 6 var diceRoll = 0
do-while 的循环版本,循环中第一步就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
1 do { 2 // move up or down for a snake or ladder 3 square += board[square] 4 // roll the dice 5 if ++diceRoll == 7 { diceRoll = 1 } 6 // move by the rolled amount 7 square += diceRoll 8 } while square < finalSquare 9 println("Game over!")
1 var temperatureInFahrenheit = 30 2 if temperatureInFahrenheit <= 32 { 3 println("It's very cold. Consider wearing a scarf.") 4 } 5 // prints "It's very cold. Consider wearing a scarf."
1 temperatureInFahrenheit = 40 2 if temperatureInFahrenheit <= 32 { 3 println("It's very cold. Consider wearing a scarf.") 4 } else { 5 println("It's not that cold. Wear a t-shirt.") 6 } 7 // prints "It's not that cold. Wear a t-shirt."
显然,这两条分支中总有一条会被执行。由于温度已升至40华氏度,不算太冷,没必要再围围巾——因此,else分支就被触发了。
1 temperatureInFahrenheit = 90 2 if temperatureInFahrenheit <= 32 { 3 println("It's very cold. Consider wearing a scarf.") 4 } else if temperatureInFahrenheit >= 86 { 5 println("It's really warm. Don't forget to wear sunscreen.") 6 } else { 7 println("It's not that cold. Wear a t-shirt.") 8 } 9 // prints "It's really warm. Don't forget to wear sunscreen."
在上面的例子中,额外的if语句用于判断是不是特别热。而最后的else语句被保留了下来,用于打印既不冷也不热时的消息。
1 temperatureInFahrenheit = 72 2 if temperatureInFahrenheit <= 32 { 3 println("It's very cold. Consider wearing a scarf.") 4 } else if temperatureInFahrenheit >= 86 { 5 println("It's really warm. Don't forget to wear sunscreen.") 6 }
1 switch `some value to consider` { 2 case `value 1`: 3 `respond to value 1` 4 case `value 2`, 5 `value 3`: 6 `respond to value 2 or 3` 7 default: 8 `otherwise, do something else` 9 }
1 let someCharacter: Character = "e" 2 switch someCharacter { 3 case "a", "e", "i", "o", "u": 4 println("\(someCharacter) is a vowel") 5 case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", 6 "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": 7 println("\(someCharacter) is a consonant") 8 default: 9 println("\(someCharacter) is not a vowel or a consonant") 10 } 11 // prints "e is a vowel"
在这个例子中,第一个case块用于匹配五个元音,第二个case块用于匹配所有的辅音。
1 let anotherCharacter: Character = "a" 2 switch anotherCharacter { 3 case "a": 4 case "A": 5 println("The letter A") 6 default: 7 println("Not the letter A") 8 } 9 // this will report a compile-time error
不像C语言里的switch语句,在 Swift 中,switch语句不会同时匹配"a"和"A"。相反的,上面的代码会引起编译期错误:case "a": does not contain any executable statements——这就避免了意外地从一个case块贯穿到另外一个,使得代码更安全、也更直观。
1 switch `some value to consider` { 2 case `value 1`, 3 `value 2`: 4 `statements` 5 }
1 let count = 3_000_000_000_000 2 let countedThings = "stars in the Milky Way" 3 var naturalCount: String 4 switch count { 5 case 0: 6 naturalCount = "no" 7 case 1...3: 8 naturalCount = "a few" 9 case 4...9: 10 naturalCount = "several" 11 case 10...99: 12 naturalCount = "tens of" 13 case 100...999: 14 naturalCount = "hundreds of" 15 case 1000...999_999: 16 naturalCount = "thousands of" 17 default: 18 naturalCount = "millions and millions of" 19 } 20 println("There are \(naturalCount) \(countedThings).") 21 // prints "There are millions and millions of stars in the Milky Way."
1 let somePoint = (1, 1) 2 switch somePoint { 3 case (0, 0): 4 println("(0, 0) is at the origin") 5 case (_, 0): 6 println("(\(somePoint.0), 0) is on the x-axis") 7 case (0, _): 8 println("(0, \(somePoint.1)) is on the y-axis") 9 case (-2...2, -2...2): 10 println("(\(somePoint.0), \(somePoint.1)) is inside the box") 11 default: 12 println("(\(somePoint.0), \(somePoint.1)) is outside of the box") 13 } 14 // prints "(1, 1) is inside the box"
1 let anotherPoint = (2, 0) 2 switch anotherPoint { 3 case (let x, 0): 4 println("on the x-axis with an x value of \(x)") 5 case (0, let y): 6 println("on the y-axis with a y value of \(y)") 7 case let (x, y): 8 println("somewhere else at (\(x), \(y))") 9 } 10 // prints "on the x-axis with an x value of 2"
1 let yetAnotherPoint = (1, -1) 2 switch yetAnotherPoint { 3 case let (x, y) where x == y: 4 println("(\(x), \(y)) is on the line x == y") 5 case let (x, y) where x == -y: 6 println("(\(x), \(y)) is on the line x == -y") 7 case let (x, y): 8 println("(\(x), \(y)) is just some arbitrary point") 9 } 10 // prints "(1, -1) is on the line x == -y"
1 let puzzleInput = "great minds think alike" 2 var puzzleOutput = "" 3 for character in puzzleInput { 4 switch character { 5 case "a", "e", "i", "o", "u", " ": 6 continue 7 default: 8 puzzleOutput += character 9 } 10 } 11 println(puzzleOutput) 12 // prints "grtmndsthnklk"
在上面的代码中,只要匹配到元音字母或者空格字符,就调用continue语句,使本次循环迭代结束,从新开始下次循环迭代。这种行为使switch匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。
1 let numberSymbol: Character = "三" // Simplified Chinese for the number 3 2 possibleIntegerValue: Int? 3 switch numberSymbol { 4 case "1", "?", "一", "?": 5 possibleIntegerValue = 1 6 case "2", "?", "二", "?": 7 possibleIntegerValue = 2 8 case "3", "?", "三", "?": 9 possibleIntegerValue = 3 10 case "4", "?", "四", "?": 11 possibleIntegerValue = 4 12 default: 13 break 14 } 15 if let integerValue = possibleIntegerValue { 16 println("The integer value of \(numberSymbol) is \(integerValue).") 17 } else { 18 println("An integer value could not be found for \(numberSymbol).") 19 } 20 // prints "The integer value of 三 is 3."
这个例子检查numberSymbol是否是拉丁,阿拉伯,中文或者泰语中的1...4之一。如果被匹配到,该switch分支语句给Int?类型变量possibleIntegerValue设置一个整数值。
1 let integerToDescribe = 5 2 var description = "The number \(integerToDescribe) is" 3 switch integerToDescribe { 4 case 2, 3, 5, 7, 11, 13, 17, 19: 5 description += " a prime number, and also" 6 fallthrough 7 default: 8 description += " an integer." 9 } 10 println(description) 11 // prints "The number 5 is a prime number, and also an integer."
这个例子定义了一个String类型的变量description并且给它设置了一个初始值。函数使用switch逻辑来判断integerToDescribe变量的值。当integerToDescribe的值属于列表中的质数之一时,该函数添加一段文字在description后,来表明这个是数字是一个质数。然后它使用fallthrough关键字来"落入"到default分支中。default分支添加一段额外的文字在description的最后,至此switch代码块执行完了。
1 label name: while condition { 2 statements 3 }
1 let finalSquare = 25 2 var board = Int[](count: finalSquare + 1, repeatedValue: 0) 3 board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 4 board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 5 var square = 0 6 var diceRoll = 0
这个版本的游戏使用while循环体和switch方法块来实现游戏的逻辑。while循环体有一个标签名gameLoop,来表明它是蛇梯棋游戏的主循环。
1 gameLoop: while square != finalSquare { 2 if ++diceRoll == 7 { diceRoll = 1 } 3 switch square + diceRoll { 4 case finalSquare: 5 // diceRoll will move us to the final square, so the game is over 6 break gameLoop 7 case let newSquare where newSquare > finalSquare: 8 // diceRoll will move us beyond the final square, so roll again 9 continue gameLoop 10 default: 11 // this is a valid move, so find out its effect 12 square += diceRoll 13 square += board[square] 14 } 15 } 16 println("Game over!")
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了switch来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。