第五章ControlFlow基本翻译完成

This commit is contained in:
AlanMelody
2015-07-18 16:39:49 +08:00
parent f6533d5bd0
commit 8c59b9c844

View File

@ -1,5 +1,5 @@
> 翻译:[vclwei](https://github.com/vclwei), [coverxit](https://github.com/coverxit), [NicePiao](https://github.com/NicePiao) > 翻译:[vclwei](https://github.com/vclwei), [coverxit](https://github.com/coverxit), [NicePiao](https://github.com/NicePiao), [JackAlan](https://github.com/AlanMelody)
> 校对:[coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai) > 校对:[coverxit](https://github.com/coverxit), [stanzhai](https://github.com/stanzhai), [JackAlan](https://github.com/AlanMelody)
# 控制流 # 控制流
----------------- -----------------
@ -11,19 +11,19 @@
- [条件语句](#conditional_statement) - [条件语句](#conditional_statement)
- [控制转移语句Control Transfer Statements](#control_transfer_statements) - [控制转移语句Control Transfer Statements](#control_transfer_statements)
Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的`for``while`循环,基于特定条件选择执行不同代码分支的`if``switch`语句,还有控制流程跳转到其他代码的`break``continue`语句。 Swift提供了类似 C 语言的流程控制结构,包括可以多次执行任务的`for``while`循环,基于特定条件选择执行不同代码分支的`if``guard``switch`语句,还有控制流程跳转到其他代码的`break``continue`语句。
除了 C 语言里面传统的 for 条件递增(`for-condition-increment`循环Swift 还增加了`for-in`循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。 除了 C 语言里面传统的 for 循环Swift 还增加了`for-in`循环用来更简单地遍历数组array字典dictionary区间range字符串string和其他序列类型。
Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿fallthrough至下一个 caseSwift 无需写`break`,所以不会发生这种贯穿fallthrough的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。 Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某个 case 不小心漏写了`break`,这个 case 就会贯穿至下一个 caseSwift 无需写`break`所以不会发生这种贯穿的情况。case 还可以匹配更多的类型模式包括区间匹配range matching元组tuple和特定类型的描述。`switch`的 case 语句中匹配的值可以是由 case 体内部临时的常量或者变量决定,也可以由`where`分句描述更复杂的匹配条件。
<a name="for_loops"></a> <a name="for_loops"></a>
## For 循环 ## For 循环
`for`循环来按照指定的次数多次执行一系列语句。Swift 提供两种`for`循环形式 Swift 提供两种`for`循环形式以来按照指定的次数多次执行一系列语句:
* `for-in`用来遍历一个区间range序列sequence集合collection系列progression里面所有的元素执行一系列语句。 * `for-in`循环对一个集合里面的每个元素执行一系列语句。
* for条件递增(`for-condition-increment`)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。 * for循环,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
<a name="for_in"></a> <a name="for_in"></a>
### For-In ### For-In
@ -34,7 +34,7 @@ Swift 的`switch`语句比 C 语言中更加强大。在 C 语言中,如果某
```swift ```swift
for index in 1...5 { for index in 1...5 {
println("\(index) times 5 is \(index * 5)") print("\(index) times 5 is \(index * 5)")
} }
// 1 times 5 is 5 // 1 times 5 is 5
// 2 times 5 is 10 // 2 times 5 is 10
@ -43,13 +43,10 @@ for index in 1...5 {
// 5 times 5 is 25 // 5 times 5 is 25
``` ```
例子中用来进行遍历的元素是一组使用闭区间操作符(`...`)表示的从`1``5`的数字。`index`被赋值为闭区间中的第一个数字(`1`),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前`index`值所对应的乘 5 乘法表结果。该语句执行后,`index`的值被更新为闭区间中的第二个数字(`2`),之后`println`方法会再执行一次。整个过程会进行到闭区间结尾为止。 例子中用来进行遍历的元素是一组使用闭区间操作符(`...`)表示的从`1``5`的数字。`index`被赋值为闭区间中的第一个数字(`1`),然后循环中的语句被执行一次。在本例中,这个循环只包含一个语句,用来输出当前`index`值所对应的乘 5 乘法表结果。该语句执行后,`index`的值被更新为闭区间中的第二个数字(`2`),之后`print(_:)`函数会再执行一次。整个过程会进行到闭区间结尾为止。
上面的例子中,`index`是一个每次循环遍历开始时被自动赋值的常量。这种情况下,`index`在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用`let`关键字声明。 上面的例子中,`index`是一个每次循环遍历开始时被自动赋值的常量。这种情况下,`index`在使用前不需要声明,只需要将它包含在循环的声明中,就可以对其进行隐式声明,而无需使用`let`关键字声明。
>注意:
`index`常量只存在于循环的生命周期里。如果你想在循环完成后访问`index`的值,又或者想让`index`成为一个变量而不是常量,你必须在循环之前自己进行声明。
如果你不需要知道区间内每一项的值,你可以使用下划线(`_`)替代变量名来忽略对值的访问: 如果你不需要知道区间内每一项的值,你可以使用下划线(`_`)替代变量名来忽略对值的访问:
```swift ```swift
@ -59,7 +56,7 @@ var answer = 1
for _ in 1...power { for _ in 1...power {
answer *= base answer *= base
} }
println("\(base) to the power of \(power) is \(answer)") print("\(base) to the power of \(power) is \(answer)")
// 输出 "3 to the power of 10 is 59049" // 输出 "3 to the power of 10 is 59049"
``` ```
@ -70,7 +67,7 @@ println("\(base) to the power of \(power) is \(answer)")
```swift ```swift
let names = ["Anna", "Alex", "Brian", "Jack"] let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names { for name in names {
println("Hello, \(name)!") print("Hello, \(name)!")
} }
// Hello, Anna! // Hello, Anna!
// Hello, Alex! // Hello, Alex!
@ -78,41 +75,27 @@ for name in names {
// Hello, Jack! // Hello, Jack!
``` ```
你也可以通过遍历一个字典来访问它的键值对key-value pairs。遍历字典时,字典的每项元素会以`(key, value)`元组的形式返回,你可以在`for-in`循环中使用显式的常量名称来解读`(key, value)`元组。下面的例子中字典的键key解读为常量`animalName`,字典的值会被解读为常量`legCount` 你也可以通过遍历一个字典来访问它的键值对。遍历字典时,字典的每项元素会以`(key, value)`元组的形式返回,你可以在`for-in`循环中使用显式的常量名称来解读`(key, value)`元组。下面的例子中字典的键key解读为常量`animalName`,字典的值会被解读为常量`legCount`
```swift ```swift
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4] let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs { for (animalName, legCount) in numberOfLegs {
println("\(animalName)s have \(legCount) legs") print("\(animalName)s have \(legCount) legs")
} }
// spiders have 8 legs
// ants have 6 legs // ants have 6 legs
// cats have 4 legs // cats have 4 legs
// spiders have 8 legs
``` ```
字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](../chapter2/04_Collection_Types.html)。 字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。关于数组和字典,详情参见[集合类型](04_Collection_Types.html)。
除了数组和字典,你也可以使用`for-in`循环来遍历字符串中的字符(`Character` <a name="for"></a>
```swift
for character in "Hello" {
println(character)
}
// H
// e
// l
// l
// o
```
<a name="for_condition_increment"></a>
### For条件递增for-condition-increment
除了`for-in`循环Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环: 除了`for-in`循环Swift 提供使用条件判断和递增方法的标准 C 样式`for`循环:
```swift ```swift
for var index = 0; index < 3; ++index { for var index = 0; index < 3; ++index {
println("index is \(index)") print("index is \(index)")
} }
// index is 0 // index is 0
// index is 1 // index is 1
@ -129,30 +112,23 @@ for var index = 0; index < 3; ++index {
这个循环执行流程如下: 这个循环执行流程如下:
1. 循环首次启动时,初始化表达式_initialization expression_被调用一次,用来初始化循环所需的所有常量和变量。 1. 循环首次启动时,初始化表达式被调用一次,用来初始化循环所需的所有常量和变量。
2. 条件表达式_condition expression_被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号 2. 条件表达式被调用,如果表达式调用结果为`false`,循环结束,继续执行`for`循环关闭大括号
`}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码_statements_ `}`)之后的代码。如果表达式调用结果为`true`,则会执行大括号内部的代码。
3. 执行所有语句_statements_之后执行递增表达式_increment expression_。通常会增加或减少计数器的值,或者根据语句_statements_输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。 3. 执行所有语句之后,执行递增表达式。通常会增加或减少计数器的值,或者根据语句输出来修改某一个初始化的变量。当递增表达式运行完成后,重复执行第 2 步,条件表达式会再次执行。
上述描述和循环格式等同于:
> `initialization`
> while `condition` {
> `statements`
> `increment`
> }
在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index` 在初始化表达式中声明的常量和变量(比如`var index = 0`)只在`for`循环的生命周期里有效。如果想在循环结束后访问`index`的值,你必须要在循环生命周期开始前声明`index`
```swift ```swift
var index: Int var index: Int
for index = 0; index < 3; ++index { for index = 0; index < 3; ++index {
println("index is \(index)") print("index is \(index)")
} }
// index is 0 // index is 0
// index is 1 // index is 1
// index is 2 // index is 2
println("The loop statements were executed \(index) times") print("The loop statements were executed \(index) times")
// 输出 "The loop statements were executed 3 times // 输出 "The loop statements were executed 3 times
``` ```
@ -177,7 +153,7 @@ println("The loop statements were executed \(index) times")
> `statements` > `statements`
> } > }
下面的例子来玩一个叫做_蛇和梯子Snakes and Ladders_的小游戏,也叫做_滑道和梯子Chutes and Ladders_ 下面的例子来玩一个叫做蛇和梯子的小游戏,也叫做滑道和梯子:
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) ![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png)
@ -219,7 +195,7 @@ while square < finalSquare {
square += board[square] square += board[square]
} }
} }
println("Game over!") print("Game over!")
``` ```
本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。`++diceRoll`调用完成_后_返回值等于`diceRoll`自增后的值。任何时候如果`diceRoll`的值等于7时就超过了骰子的最大值会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1``2``3``4``5``6``1``2` 本例中使用了最简单的方法来模拟掷骰子。 `diceRoll`的值并不是一个随机数,而是以`0`为初始值,之后每一次`while`循环,`diceRoll`的值使用前置自增操作符(`++i`)来自增 1 ,然后检测是否超出了最大值。`++diceRoll`调用完成_后_返回值等于`diceRoll`自增后的值。任何时候如果`diceRoll`的值等于7时就超过了骰子的最大值会被重置为`1`。所以`diceRoll`的取值顺序会一直是`1``2``3``4``5``6``1``2`
@ -233,18 +209,21 @@ println("Game over!")
`while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏的长度或者循环的次数,只有在达成指定条件时循环才会结束。 `while` 循环比较适合本例中的这种情况,因为在 `while` 循环开始时,我们并不知道游戏的长度或者循环的次数,只有在达成指定条件时循环才会结束。
<a name="do_while"></a> <a name="repeat_while"></a>
###Do-While ###Repeat-While
`while`循环的另外一种形式是`do-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为`false` `while`循环的另外一种形式是`repeat-while`,它和`while`的区别是在判断循环条件之前,先执行一次循环的代码块,然后重复循环直到条件为`false`
下面是一般情况下 `do-while`循环的格式 > 注意
> Swift语言的`repeat-while`循环合其他语言中的`do-while`循环是类似的。
> do { 下面是一般情况下 `repeat-while`循环的格式:
> repeat {
> `statements` > `statements`
> } while `condition` > } while `condition`
还是蛇和梯子的游戏,使用`do-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环一样: 还是蛇和梯子的游戏,使用`repeat-while`循环来替代`while`循环。`finalSquare``board``square``diceRoll`的值初始化同`while`循环一样:
``` swift ``` swift
let finalSquare = 25 let finalSquare = 25
@ -255,12 +234,12 @@ var square = 0
var diceRoll = 0 var diceRoll = 0
``` ```
`do-while`的循环版本循环中_第一步_就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。 `repeat-while`的循环版本循环中_第一步_就需要去检测是否在梯子或者蛇的方块上。没有梯子会让玩家直接上到第 25 个方格,所以玩家不会通过梯子直接赢得游戏。这样在循环开始时先检测是否踩在梯子或者蛇上是安全的。
游戏开始时,玩家在第 0 个方格上,`board[0]`一直等于 0 不会有什么影响: 游戏开始时,玩家在第 0 个方格上,`board[0]`一直等于 0 不会有什么影响:
```swift ```swift
do { repeat {
// 顺着梯子爬上去或者顺着蛇滑下去 // 顺着梯子爬上去或者顺着蛇滑下去
square += board[square] square += board[square]
// 掷骰子 // 掷骰子
@ -268,12 +247,12 @@ do {
// 根据点数移动 // 根据点数移动
square += diceRoll square += diceRoll
} while square < finalSquare } while square < finalSquare
println("Game over!") print("Game over!")
``` ```
检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动`diceRoll`个方格,本轮循环结束。 检测完玩家是否踩在梯子或者蛇上之后,开始掷骰子,然后玩家向前移动`diceRoll`个方格,本轮循环结束。
循环条件(`while square < finalSquare`)和`while`方式相同,但是只会在循环结束后进行计算。在这个游戏中,`do-while`表现得比`while`循环更好。`do-while`方式会在条件判断`square`没有超出后直接运行`square += board[square]`,这种方式可以去掉`while`版本中的数组越界判断。 循环条件(`while square < finalSquare`)和`while`方式相同,但是只会在循环结束后进行计算。在这个游戏中,`repeat-while`表现得比`while`循环更好。`repeat-while`方式会在条件判断`square`没有超出后直接运行`square += board[square]`,这种方式可以去掉`while`版本中的数组越界判断。
<a name="conditional_statement"></a> <a name="conditional_statement"></a>
## 条件语句 ## 条件语句
@ -290,7 +269,7 @@ Swift 提供两种类型的条件语句:`if`语句和`switch`语句。通常
```swift ```swift
var temperatureInFahrenheit = 30 var temperatureInFahrenheit = 30
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} }
// 输出 "It's very cold. Consider wearing a scarf." // 输出 "It's very cold. Consider wearing a scarf."
``` ```
@ -302,9 +281,9 @@ if temperatureInFahrenheit <= 32 {
```swift ```swift
temperatureInFahrenheit = 40 temperatureInFahrenheit = 40
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} else { } else {
println("It's not that cold. Wear a t-shirt.") print("It's not that cold. Wear a t-shirt.")
} }
// 输出 "It's not that cold. Wear a t-shirt." // 输出 "It's not that cold. Wear a t-shirt."
``` ```
@ -316,11 +295,11 @@ if temperatureInFahrenheit <= 32 {
```swift ```swift
temperatureInFahrenheit = 90 temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 { } else if temperatureInFahrenheit >= 86 {
println("It's really warm. Don't forget to wear sunscreen.") print("It's really warm. Don't forget to wear sunscreen.")
} else { } else {
println("It's not that cold. Wear a t-shirt.") print("It's not that cold. Wear a t-shirt.")
} }
// 输出 "It's really warm. Don't forget to wear sunscreen." // 输出 "It's really warm. Don't forget to wear sunscreen."
``` ```
@ -330,11 +309,11 @@ if temperatureInFahrenheit <= 32 {
实际上,最后的`else`语句是可选的: 实际上,最后的`else`语句是可选的:
```swift ```swift
temperatureInFahrenheit = 72 temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 { if temperatureInFahrenheit <= 32 {
println("It's very cold. Consider wearing a scarf.") print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 { } else if temperatureInFahrenheit >= 86 {
println("It's really warm. Don't forget to wear sunscreen.") print("It's really warm. Don't forget to wear sunscreen.")
} }
``` ```
@ -361,7 +340,7 @@ if temperatureInFahrenheit <= 32 {
每一个 case 都是代码执行的一条分支,这与`if`语句类似。与之不同的是,`switch`语句会决定哪一条分支应该被执行。 每一个 case 都是代码执行的一条分支,这与`if`语句类似。与之不同的是,`switch`语句会决定哪一条分支应该被执行。
`switch`语句必须是_完备的_。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面。 `switch`语句必须是完备的。这就是说,每一个可能的值都必须至少有一个 case 分支与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认(`default`)分支满足该要求,这个默认分支必须在`switch`语句的最后面。
下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符: 下面的例子使用`switch`语句来匹配一个名为`someCharacter`的小写字符:
@ -369,12 +348,12 @@ if temperatureInFahrenheit <= 32 {
let someCharacter: Character = "e" let someCharacter: Character = "e"
switch someCharacter { switch someCharacter {
case "a", "e", "i", "o", "u": case "a", "e", "i", "o", "u":
println("\(someCharacter) is a vowel") print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
println("\(someCharacter) is a consonant") print("\(someCharacter) is a consonant")
default: default:
println("\(someCharacter) is not a vowel or a consonant") print("\(someCharacter) is not a vowel or a consonant")
} }
// 输出 "e is a vowel" // 输出 "e is a vowel"
``` ```
@ -389,7 +368,7 @@ default:
与 C 语言和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。 与 C 语言和 Objective-C 中的`switch`语句不同,在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止`switch`语句,而不会继续执行下一个 case 分支。这也就是说,不需要在 case 分支中显式地使用`break`语句。这使得`switch`语句更安全、更易用,也避免了因忘记写`break`语句而产生的错误。
> 注意: > 注意:
你依然可以在 case 分支中的代码执行完毕前跳出,详情请参[Switch 语句中的 break](#break_in_a_switch_statement)。 虽然在Swift中`break`不是必须的,但你依然可以在 case 分支中的代码执行完毕前使用`break`跳出,详情请参[Switch 语句中的 break](#break_in_a_switch_statement)。
每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的: 每一个 case 分支都*必须*包含至少一条语句。像下面这样书写代码是无效的,因为第一个 case 分支是空的:
@ -398,9 +377,9 @@ let anotherCharacter: Character = "a"
switch anotherCharacter { switch anotherCharacter {
case "a": case "a":
case "A": case "A":
println("The letter A") print("The letter A")
default: default:
println("Not the letter A") print("Not the letter A")
} }
// this will report a compile-time error // this will report a compile-time error
``` ```
@ -418,39 +397,42 @@ default:
> 注意: > 注意:
如果想要贯穿至特定的 case 分支中,请使用`fallthrough`语句,详情请参考[贯穿Fallthrough](#fallthrough)。 如果想要贯穿至特定的 case 分支中,请使用`fallthrough`语句,详情请参考[贯穿Fallthrough](#fallthrough)。
<a name="range_matching"></a> <a name="interval_matching"></a>
#### 区间匹配Range Matching #### 区间匹配
case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式: case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式:
```swift ```
let count = 3_000_000_000_000 let approximateCount = 62
let countedThings = "stars in the Milky Way" let countedThings = "moons orbiting Saturn"
var naturalCount: String var naturalCount: String
switch count { switch approximateCount {
case 0: case 0:
naturalCount = "no" naturalCount = "no"
case 1...3: case 1..<5:
naturalCount = "a few" naturalCount = "a few"
case 4...9: case 5..<12:
naturalCount = "several" naturalCount = "several"
case 10...99: case 12..<100:
naturalCount = "tens of" naturalCount = "dozens of"
case 100...999: case 100..<1000:
naturalCount = "hundreds of" naturalCount = "hundreds of"
case 1000...999_999:
naturalCount = "thousands of"
default: default:
naturalCount = "millions and millions of" naturalCount = "many"
} }
println("There are \(naturalCount) \(countedThings).") print("There are \(naturalCount) \(countedThings).")
// 输出 "There are millions and millions of stars in the Milky Way." // 输出 "There are dozens of moons orbiting Saturn."
``` ```
在上例中,`approximateCount`在一个`switch`声明中被估值。每一个`case`都与之进行比较。因为`approximateCount`落在了12到100的区间所以`naturalCount`等于`"dozens of"`值,并且此后这段执行跳出了`switch`声明。
> 注意:
> 闭区间操作符(`...`)以及半开区间操作符(`..<`)功能被重载去返回`IntervalType`或`Range`。一个区间可以决定他是否包含特定的元素,就像当匹配一个`switch`声明的`case`一样。区间是一个连续值的集合,可以用`for-in`语句遍历它。
<a name="tuples"></a> <a name="tuples"></a>
#### 元组Tuple #### 元组Tuple
可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。 我们可以使用元组在同一个`switch`语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线(`_`)来匹配所有可能的值。
下面的例子展示了如何使用一个`(Int, Int)`类型的元组来分类下图中的点(x, y) 下面的例子展示了如何使用一个`(Int, Int)`类型的元组来分类下图中的点(x, y)
@ -458,15 +440,15 @@ println("There are \(naturalCount) \(countedThings).")
let somePoint = (1, 1) let somePoint = (1, 1)
switch somePoint { switch somePoint {
case (0, 0): case (0, 0):
println("(0, 0) is at the origin") print("(0, 0) is at the origin")
case (_, 0): case (_, 0):
println("(\(somePoint.0), 0) is on the x-axis") print("(\(somePoint.0), 0) is on the x-axis")
case (0, _): case (0, _):
println("(0, \(somePoint.1)) is on the y-axis") print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2): case (-2...2, -2...2):
println("(\(somePoint.0), \(somePoint.1)) is inside the box") print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default: default:
println("(\(somePoint.0), \(somePoint.1)) is outside of the box") print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
} }
// 输出 "(1, 1) is inside the box" // 输出 "(1, 1) is inside the box"
``` ```
@ -489,11 +471,11 @@ case 分支的模式允许将匹配的值绑定到一个临时的常量或变量
let anotherPoint = (2, 0) let anotherPoint = (2, 0)
switch anotherPoint { switch anotherPoint {
case (let x, 0): case (let x, 0):
println("on the x-axis with an x value of \(x)") print("on the x-axis with an x value of \(x)")
case (0, let y): case (0, let y):
println("on the y-axis with a y value of \(y)") print("on the y-axis with a y value of \(y)")
case let (x, y): case let (x, y):
println("somewhere else at (\(x), \(y))") print("somewhere else at (\(x), \(y))")
} }
// 输出 "on the x-axis with an x value of 2" // 输出 "on the x-axis with an x value of 2"
``` ```
@ -504,7 +486,7 @@ case let (x, y):
这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`anotherPoint`的一个或两个值。第一个 case ——`case (let x, 0)`将匹配一个纵坐标为`0`的点,并把这个点的横坐标赋给临时的常量`x`。类似的,第二个 case ——`case (0, let y)`将匹配一个横坐标为`0`的点,并把这个点的纵坐标赋给临时的常量`y`。 这三个 case 都声明了常量`x`和`y`的占位符,用于临时获取元组`anotherPoint`的一个或两个值。第一个 case ——`case (let x, 0)`将匹配一个纵坐标为`0`的点,并把这个点的横坐标赋给临时的常量`x`。类似的,第二个 case ——`case (0, let y)`将匹配一个横坐标为`0`的点,并把这个点的纵坐标赋给临时的常量`y`。
一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里引用。在这个例子中,它们用于简化`println`的书写。 一旦声明了这些临时的常量,它们就可以在其对应的 case 分支里引用。在这个例子中,它们用于简化`print(_:)`的书写。
请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。 请注意,这个`switch`语句不包含默认分支。这是因为最后一个 case ——`case let(x, y)`声明了一个可以匹配余下所有值的元组。这使得`switch`语句已经完备了,因此不需要再书写默认分支。
@ -521,11 +503,11 @@ case 分支的模式可以使用`where`语句来判断额外的条件。
let yetAnotherPoint = (1, -1) let yetAnotherPoint = (1, -1)
switch yetAnotherPoint { switch yetAnotherPoint {
case let (x, y) where x == y: case let (x, y) where x == y:
println("(\(x), \(y)) is on the line x == y") print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y: case let (x, y) where x == -y:
println("(\(x), \(y)) is on the line x == -y") print("(\(x), \(y)) is on the line x == -y")
case let (x, y): case let (x, y):
println("(\(x), \(y)) is just some arbitrary point") print("(\(x), \(y)) is just some arbitrary point")
} }
// 输出 "(1, -1) is on the line x == -y" // 输出 "(1, -1) is on the line x == -y"
``` ```
@ -543,12 +525,12 @@ case let (x, y):
控制转移语句改变你代码的执行顺序通过它你可以实现代码的跳转。Swift有四种控制转移语句。 控制转移语句改变你代码的执行顺序通过它你可以实现代码的跳转。Swift有四种控制转移语句。
- continue - `continue`
- break - `break`
- fallthrough - `fallthrough`
- return - `return`
我们将会在下面讨论`continue`、`break`和`fallthrough`语句。`return`语句将会在[函数](../chapter2/06_Functions.html)章节讨论。 我们将会在下面讨论`continue`、`break`和`fallthrough`语句。`return`语句将会在[函数](06_Functions.html)章节讨论。
<a name="continue"></a> <a name="continue"></a>
### Continue ### Continue
@ -556,7 +538,7 @@ case let (x, y):
`continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。 `continue`语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。
> 注意: > 注意:
在一个for条件递增`for-condition-increment`循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。 > 在一个带有条件递增的for循环体中,调用`continue`语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。
下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句: 下面的例子把一个小写字符串中的元音字母和空格字符移除,生成了一个含义模糊的短句:
@ -571,7 +553,7 @@ for character in puzzleInput {
puzzleOutput.append(character) puzzleOutput.append(character)
} }
} }
println(puzzleOutput) print(puzzleOutput)
// 输出 "grtmndsthnklk" // 输出 "grtmndsthnklk"
``` ```
@ -615,9 +597,9 @@ default:
break break
} }
if let integerValue = possibleIntegerValue { if let integerValue = possibleIntegerValue {
println("The integer value of \(numberSymbol) is \(integerValue).") print("The integer value of \(numberSymbol) is \(integerValue).")
} else { } else {
println("An integer value could not be found for \(numberSymbol).") print("An integer value could not be found for \(numberSymbol).")
} }
// 输出 "The integer value of 三 is 3." // 输出 "The integer value of 三 is 3."
``` ```
@ -633,7 +615,7 @@ if let integerValue = possibleIntegerValue {
Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显示的插入`break`语句到每个`switch`分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。 Swift 中的`switch`不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个`switch`代码块完成了它的执行。相比之下C 语言要求你显示的插入`break`语句到每个`switch`分支的末尾来阻止自动落入到下一个 case 分支中。Swift 的这种避免默认落入到下一个分支中的特性意味着它的`switch` 功能要比 C 语言的更加清晰和可预测,可以避免无意识地执行多个 case 分支从而引发的错误。
如果你确实需要 C 风格的贯穿fallthrough的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。 如果你确实需要 C 风格的贯穿的特性,你可以在每个需要该特性的 case 分支中使用`fallthrough`关键字。下面的例子使用`fallthrough`来创建一个数字的描述语句。
```swift ```swift
let integerToDescribe = 5 let integerToDescribe = 5
@ -645,7 +627,7 @@ case 2, 3, 5, 7, 11, 13, 17, 19:
default: default:
description += " an integer." description += " an integer."
} }
println(description) print(description)
// 输出 "The number 5 is a prime number, and also an integer." // 输出 "The number 5 is a prime number, and also an integer."
``` ```
@ -653,13 +635,13 @@ println(description)
如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到包含所有的`default`分支中。 如果`integerToDescribe`的值不属于列表中的任何质数,那么它不会匹配到第一个`switch`分支。而这里没有其他特别的分支情况,所以`integerToDescribe`匹配到包含所有的`default`分支中。
当`switch`代码块执行完后,使用`println`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。 当`switch`代码块执行完后,使用`print`函数打印该数字的描述。在这个例子中,数字`5`被准确的识别为了一个质数。
> 注意: > 注意:
`fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的`switch`语句特性是一样的。 > `fallthrough`关键字不会检查它下一个将会落入执行的 case 中的匹配条件。`fallthrough`简单地使代码执行继续连接到下一个 case 中的执行代码,这和 C 语言标准中的`switch`语句特性是一样的。
<a name="labeled_statements"></a> <a name="labeled_statements"></a>
### 带标签的语句Labeled Statements ### 带标签的语句
在 Swift 中,你可以在循环体和`switch`代码块中嵌套循环体和`switch`代码块来创造复杂的控制流结构。然而,循环体和`switch`代码块两者都可以使用`break`语句来提前结束整个方法体。因此,显示地指明`break`语句想要终止的是哪个循环体或者`switch`代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明`continue`语句想要影响哪一个循环体也会非常有用。 在 Swift 中,你可以在循环体和`switch`代码块中嵌套循环体和`switch`代码块来创造复杂的控制流结构。然而,循环体和`switch`代码块两者都可以使用`break`语句来提前结束整个方法体。因此,显示地指明`break`语句想要终止的是哪个循环体或者`switch`代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明`continue`语句想要影响哪一个循环体也会非常有用。
@ -671,9 +653,9 @@ println(description)
> `statements` > `statements`
> } > }
下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中_蛇和梯子_的改编版本。这次,游戏增加了一条额外的规则: 下面的例子是在一个带有标签的`while`循环体中调用`break`和`continue`语句,该循环体是前面章节中*蛇和梯子*的改编版本。这次,游戏增加了一条额外的规则:
- 为了获胜,你必须_刚好_落在第 25 个方块中。 - 为了获胜,你必须*刚好*落在第 25 个方块中。
如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中。 如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 25 个方块中。
@ -681,7 +663,7 @@ println(description)
![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png) ![image](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Art/snakesAndLadders_2x.png)
`finalSquare`、`board`、`square`和`diceRoll`的初始化也和之前一样 `finalSquare`、`board`、`square`和`diceRoll`值被和之前一样的方式初始化
```swift ```swift
let finalSquare = 25 let finalSquare = 25
@ -712,7 +694,7 @@ gameLoop: while square != finalSquare {
square += board[square] square += board[square]
} }
} }
println("Game over!") print("Game over!")
``` ```
每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。 每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了`switch`来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。
@ -724,3 +706,65 @@ println("Game over!")
>注意: >注意:
如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。 如果上述的`break`语句没有使用`gameLoop`标签,那么它将会中断`switch`代码块而不是`while`循环体。使用`gameLoop`标签清晰的表明了`break`想要中断的是哪个代码块。
同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。 同时请注意,当调用`continue gameLoop`去跳转到下一次循环迭代时,这里使用`gameLoop`标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以`continue`语句会影响到哪个循环体是没有歧义的。然而,`continue`语句使用`gameLoop`标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的`break gameLoop`,能够使游戏的逻辑更加清晰和易于理解。
<a name="early_exit"></a>
### 提前退出
像`if`语句一样,`guard`的执行取决于一个表达式的布尔值。我们可以使用`guard`语句来要求条件必须为真时,以执行`guard`语句后的代码。不同于`if`语句,一个`guard`语句总是有一个`else`分句,如果条件不为真则执行`else`分局中的代码。
```swift
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(["name": "John"])
// prints "Hello John!"
// prints "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// prints "Hello Jane!"
// prints "I hope the weather is nice in Cupertino."
```
如果`guard`语句的条件被满足,则在保护语句的封闭大括号结束后继续执行代码。任何使用了可选绑定作为条件的一部分并被分配了值的变量或常量对于剩下的保护语句出现的代码段是可用的。
如果条件不被满足,在`else`分支上的代码就会被执行。这个分支必须转移控制以退出`guard`语句出现的代码段。它可以用控制转移语句如`return`,`break`或`continue`做这件事,或者它调用了一个不返回的方法或函数,例如`fatalError()`。
相比于可以实现同样功能的`if`语句,按需使用`guard`语句会提升我们代码的可靠性。
它可以使你的代码连贯的被执行而不需要将它包在`else`块中,它可以使你处理违反要求的代码接近要求。
<a name="checking_api_availability"></a>
### 检测API是否可用
Swift 有内置支持去检查接口的可用性的这可以确保我们不会不小心地使用对于当前部署目标不可用的API。
编译器使用SDK中的可用信息来验证在我们在可用部署目标指定项目的代码中所有的API调用。如果我们尝试使用一个不可用的APISwift会在编译期报错。
我们使用一个可用性条件在一个`if`或`guard`语句中去有条件的执行一段代码这取决于我们想要使用的API是否在运行时是可用的。编译器使用从可用性条件语句中获取的信息这时它会去验证在代码块中调用的API是否都可用。
```swift
if #available(iOS 9, OSX 10.10, *) {
// 在 iOS 使用 iOS 9 APIs , 并且在 OS X 使用 OS X v10.10 APIs
} else {
// 回滚至早前 iOS and OS X 的API
}
```
以上可用性条件指定在iOS`if`段的代码仅仅在iOS9及更高可运行在OS X仅在OS X v10.10及更高可运行。最后一个参数,`*`,是必须的并且指定在任何其他平台上,`if`段的代码在最小可用部署目标指定项目中执行。
在它普遍的形式中,可用性条件获取了平台名字和版本的清单。平台名字可以是`iOS``OSX`或`watchOS`。除了特定的主板本号像iOS8我们可以指定较小的版本号像iOS8.3以及 OS X v10.10.3。
```swift
if #available(`platform name` `version`, `...`, *) {
`statements to execute if the APIs are available`
} else {
`fallback statements to execute if the APIs are unavailable`
}
```