Swift提供了类C语言类似的控制流结构。包括for循环和while循环来多次执行任务,if和switch语句根据不同的条件执行不同的分支代码,break和continue语句将执行流程跳转到其他语句。
除了C里面传统的for-条件-递增循环,Swift还增加了for-in循环使得遍历数组,字典,范围,字符串或者其他序列都很简单。
Swift的switch语句也要比C语言的switch更强大。Swift里面某个case语句执行完以后不会再去执行后面的case,避免C语言中由于忘记写break而引起的错误。case可以匹配多种不同的类型模式,包括范围匹配,元组或者某个特定的类型(casts to a specific type)。case当中被匹配的值可以与临时常量或者变量绑定以便在case里面可以使用,复杂的匹配条件可以使用where来表示。
1.for循环
for循环反复执行语句指定次数。Swift提供两种形式的for循环:
- for-in在某个范围,序列,集合或者系列中对每个元素执行一次。
- for-condition-increment反复执行直到满足某个特殊条件,一般在每次循环之后计数器递增。
for-in
你可以使用for-in循环来遍历集合的元素,比如一个范围的数字,某个数组中的元素或者字符串中的字符。
下面的例子输出乘法表中乘5的那一行:
- for index in 1...5 {
- println("\(index) times 5 is \(index * 5)")
- }
- // 1 times 5 is 5
- // 2 times 5 is 10
- // 3 times 5 is 15
- // 4 times 5 is 20
- // 5 times 5 is 25
上面资历中,index是常量,在每次进行循环的时候自动被赋值。这种情况index没有必要再使用前就声明。在for-in当中直接使用index就相当于是隐式声明了常量index,都不需要再使用let关键字。
NOTE 常量index的作用范围仅仅是循环体,如果你想在循环结束以后查看index的值,或者你想让index作为变量而不是常量,那你必须在循环之前自己去声明index。
如果你并不是需要范围中的所有值,你可以通过使用在变量的位置使用下划线来忽略元素的值。
- let base = 3
- let power = 10
- var answer = 1
- for _ in 1...power {
- answer *= base
- }
- println("\(base) to the power of \(power) is \(answer)")
- // prints "3 to the power of 10 is 59049"
使用for-in循环遍历数组中的元素:
- let names = ["Anna", "Alex", "Brian", "Jack"]
- for name in names {
- println("Hello, \(name)!")
- }
- // Hello, Anna!
- // Hello, Alex!
- // Hello, Brian!
- // Hello, Jack!
- let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
- for (animalName, legCount) in numberOfLegs {
- println("\(animalName)s have \(legCount) legs")
- }
- // spiders have 8 legs
- // ants have 6 legs
- // cats have 4 legs
除了数组和字典,你还可以使用for-in循环遍历字符串中的字符:
- for character in "Hello" {
- println(character)
- }
- // H
- // e
- // l
- // l
- // o
除了for-in循环,swift还支持传统的C风格的带有条件和递增语句的for循环:
- for var index = 0; index < 3; ++index {
- println("index is \(index)")
- }
- // index is 0
- // index is 1
- // index is 2
for initialization ; condition ; increment {
statements
}
和C一样分号分隔了循环定义的三个部分,但是和C不一样的是Swift不需要在initialization ; condition ; increment外面加括号。
循环的执行顺序如下:
- 当第一次进入循环的时候,初始化表达式(initialization)被执行,创建循环可能会用到的常量或者变量。
- 条件表达式被执行。如果条件表达式结果为false,循环结束,程序执行循环体以后的代码。如果条件表达式为true,就执行循环体。
- 循环体中所有语句都执行完了以后,递增语句才被执行。这条语句一般用来递增或者递减计数器,或者根据语句输出来给已经初始化过的变量更新值。当递增语句执行完以后,程序回到第二步,条件语句又被执行一次。
循环格式和执行过程可以使用下面等价的形式来表达:
initialization
while condition {
statements
increment
}
在初始化表达式中声明(比如var index = 0)的常量和变量只能在这个for循环中能够使用,要在循环结束后使用index,必须在循环之前声明index。
- var index: Int
- for index = 0; index < 3; ++index {
- println("index is \(index)")
- }
- // index is 0
- // index is 1
- // index is 2
- println("The loop statements were executed \(index) times")
- // prints "The loop statements were executed 3 times"
2.while循环
while循环反复执行循环体直到条件变为false。这种循环非常适用于在循环体执行之前循环次数未知的情况。Swift提供两种while循环形式:
- while:每次执行循环体之前先计算条件的值。
- do-while:每次执行完循环体之后计算条件的值。
while
while循环以计算条件表达式开始。如果条件为true,循环体就被反复执行知道条件为false,下面是while循环的一般形式
while condition {
statements
}
下面玩一个蛇与梯子的游:
游戏规则:
- 棋盘有25个方格,游戏的目的是要到达或者超过第25格。
- 每次一轮,你需要摇色子来决定你走几格,路线如上图虚线所示。
- 如果这一轮结束的时候你到了梯子的底部,就顺着梯子上去。
- 如果你这轮结束的时候碰到了舌头,就顺着射移到下面去。
棋盘可以使用一个Int数组来表示。棋盘大小使用常量finalSquare存储,用来初始化数组以及后面检查游戏是否通关。棋盘使用26个0来初始化,而不是25个(下标从0到25):
- let finalSquare = 25
- var board = Int[](count: finalSquare + 1, repeatedValue: 0)
- board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
- board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
第3格有一个梯子,可以爬到第11格,所以borad[03]被设置为+8,上面整数前面带加号知识为了看起来更清楚,下标小于10的前面加0只是为了代码整齐。(加号和多余的0都不是必须的,但是这里加上能让代码更清晰。)
游戏从第0格开始,就是在棋盘左下角之外。第一次掷色子就能移到棋盘上了:
- var square = 0
- var diceRoll = 0
- while square < finalSquare {
- // roll the dice
- if ++diceRoll == 7 { diceRoll = 1 }
- // move by the rolled amount
- square += diceRoll
- if square < board.count {
- // if we're still on the board, move up or down for a snake or a ladder
- square += board[square]
- }
- }
- println("通关!")
色子投出来以后,玩家移动diceRoll对应的格数。如果玩家移动以后的格数大于或者等于25就赢了。考虑这种情况,代码在移动以后检测当前位置是不是比棋盘格数要小,如果比棋盘格数小才加上那一格中的值,如果不小,就说明过关了。那一格中存储的值就是顺着梯子往上爬或者顺着蛇往下爬的格数。如果不加这个判断,board[square]就有可能要去获取数组board范围外的值,将会引起运行错误。如果现在squre等于26,代码会去获取board[26],这个下标超过数组board的范围了。
当前循环体执行完了以后,执行循环条件语句,看循环是否继续执行。如果玩家已经移动到或者超过第25格,循环条件结果就是false,游戏就结束了。
这个例子使用while循环是比较合适的,因为在循环开始之前是不知道游戏要进行的次数的。相反,循环是一直执行到某个特定的条件满足才停止。
do-while
另一种while循环do-while先执行一次循环体,然后才去判断循环条件。然后循环执行循环体直到循环条件为false。
下面是do-while的一般形式:
do {
statements
} while condition
下面还是蛇与梯子的游戏,这次使用do-while而不是while了。finalSquare,board,square和diceRoll的初始化方式还和上面一样:
- let finalSquare = 25
- var board = Int[](count: finalSquare + 1, repeatedValue: 0)
- 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
这次游戏循环第一步就是检查是否在梯子或蛇上。因为没有梯子可以直接通到第25格,所以玩家不可能通过梯子移动就直接通关。所以循环第一步直接检查蛇或者梯子是安全的。(这里读起来挺别扭的好像没有解释清楚,应该是说用do-while的时候循环第一步就可以直接加上当前格子对应的数字,而不是先掷色子,因为掷色子移动然后就可能到达25格,那你再爬梯子之前就需要前面那个if了,但是如果先爬梯子,就不用判断接着就掷色子,因为从图上看出来爬梯子不可能会通关的。)
游戏开始的时候玩家在第0格上,board[0]还是0,没有什么影响:
- do {
- <span style="white-space:pre"> </span>// move up or down for a snake or ladder
- <span style="white-space:pre"> </span>square += board[square]
- <span style="white-space:pre"> </span>// roll the dice
- <span style="white-space:pre"> </span>if ++diceRoll == 7 { diceRoll = 1 }
- <span style="white-space:pre"> </span>// move by the rolled amount
- <span style="white-space:pre"> </span>square += diceRoll
- } while square < finalSquare
- println("Game over!")
循环条件跟前面的是一样的,但是这次循环条件只有当第一次循环结束的时候才会被第一次计算。对于这个游戏来说do-while循环要比while循环更合适。上面do-while的循环里,当循环条件确定squre还在棋盘上的时候开始下一次循环,并且进入循环一开始就执行squre+=board[square]。这样就免去了前面对于square边界的检查。