控制流

For循环

for循环用来按照指定的次数多次执行一系列语句。Swift 提供两种for循环形式:

  • for-in用来遍历一个区间(range),序列(sequence),集合(collection),系列(progression)里面所有的元素执行一系列语句。
    1.区间
    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

2.区间(忽略对值的访问)

let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
println("(base) to the power of (power) is (answer)")
// 输出 "3 to the power of 10 is 59049"

这个计算并不需要知道每一次循环中计数器具体的值,只需要执行了正确的循环次数即可。下划线符号_(替代循环中的变量)能够忽略具体的值,并且不提供循环遍历时对值的访问。
3.遍历数组

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
println("Hello, (name)!")
}
// Hello, Anna!
// Hello, Alex!
// Hello, Brian!
// Hello, Jack!

4.遍历字典


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

字典元素的遍历顺序和插入顺序可能不同,字典的内容在内部是无序的,所以遍历元素时不能保证顺序。

5.遍历字符

for character in "Hello" {
println(character)
}
// H
// e
// l
// l
// o

  • for条件递增(for-condition-increment)语句,用来重复执行一系列语句直到达成特定条件达成,一般通过在每次循环完成后增加计数器的值来实现。
    1.for initialization; condition; increment { statements }
    for var index = 0; index < 3; ++index {
    println("index is (index)")
    }
    // index is 0
    // index is 1
    // index is 2

Switch

switch语句会尝试把某个值与若干个模式(pattern)进行匹配。根据第一个匹配成功的模式,switch语句会执行对应的代码。当有可能的情况较多时,通常用switch语句替换if语句。

  • 正常switch
    let someCharacter: Character = "e"
    switch someCharacter {
    case "a", "e", "i", "o", "u":
    println("(someCharacter) is a vowel")
    case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
    "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
    println("(someCharacter) is a consonant")
    default:
    println("(someCharacter) is not a vowel or a consonant")
    }
    // 输出 "e is a vowel"
    一个 case 也可以包含多个模式,用逗号把它们分开(如果太长了也可以分行写)
    switch some value to consider { case value 1, value 2: statements }

不存在隐式的贯穿(No Implicit Fallthrough)
在 Swift 中,当匹配的 case 分支中的代码执行完毕后,程序会终止switch语句,而不会继续执行下一个 case 分支。不需要在 case 分支中显式地使用break语句

  • 区间匹配(Range Matching)
    case 分支的模式也可以是一个值的区间。下面的例子展示了如何使用区间匹配来输出任意数字对应的自然语言格式:


    let count = 3_000_000_000_000
    let countedThings = "stars in the Milky Way"
    var naturalCount: String
    switch count {
    case 0:
    naturalCount = "no"
    case 1...3:
    naturalCount = "a few"
    case 4...9:
    naturalCount = "several"
    case 10...99:
    naturalCount = "tens of"
    case 100...999:
    naturalCount = "hundreds of"
    case 1000...999_999:
    naturalCount = "thousands of"
    default:
    naturalCount = "millions and millions of"
    }
    println("There are (naturalCount) (countedThings).")
    // 输出 "There are millions and millions of stars in the Milky Way."

  • 匹配元组(Tuple)
    你可以使用元组在同一个switch语句中测试多个值。元组中的元素可以是值,也可以是区间。另外,使用下划线()来匹配所有可能的值。

    let somePoint = (1, 1)
    switch somePoint {
    case (0, 0):
    println("(0, 0) is at the origin")
    case (
    , 0):
    println("((somePoint.0), 0) is on the x-axis")
    case (0, _):
    println("(0, (somePoint.1)) is on the y-axis")
    case (-2...2, -2...2):
    println("((somePoint.0), (somePoint.1)) is inside the box")
    default:
    println("((somePoint.0), (somePoint.1)) is outside of the box")
    }
    // 输出 "(1, 1) is inside the box"

  • 值绑定(Value Bindings)
    case 分支的模式允许将匹配的值绑定到一个临时的常量或变量,这些常量或变量在该 case 分支里就可以被引用了——这种行为被称为值绑定(value binding)。

let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
println("on the x-axis with an x value of (x)")
case (0, let y):
println("on the y-axis with a y value of (y)")
case let (x, y):
println("somewhere else at ((x), (y))")
}
// 输出 "on the x-axis with an x value of 2"

  • Where语句
    case 分支的模式可以使用where语句来判断额外的条件。
    let yetAnotherPoint = (1, -1)
    switch yetAnotherPoint {
    case let (x, y) where x == y:
    println("((x), (y)) is on the line x == y")
    case let (x, y) where x == -y:
    println("((x), (y)) is on the line x == -y")
    case let (x, y):
    println("((x), (y)) is just some arbitrary point")
    }
    // 输出 "(1, -1) is on the line x == -y"

控制转移语句(Control Transfer Statements)

控制转移语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Swift有四种控制转移语句。

  • continue

continue语句告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。就好像在说“本次循环迭代我已经执行完了”,但是并不会离开整个循环体。

注意:

在一个for条件递增(for-condition-increment)循环体中,在调用continue语句后,迭代增量仍然会被计算求值。循环体继续像往常一样工作,仅仅只是循环体中的执行代码会被跳过。

let puzzleInput = "great minds think alike"
var puzzleOutput = ""
for character in puzzleInput {
switch character {
case "a", "e", "i", "o", "u", " ":
continue
default:
puzzleOutput.append(character)
}
}
println(puzzleOutput)
// 输出 "grtmndsthnklk"

在上面的代码中,只要匹配到元音字母或者空格字符,就调用continue语句,使本次循环迭代结束,从新开始下次循环迭代。这种行为使switch匹配到元音字母和空格字符时不做处理,而不是让每一个匹配到的字符都被打印。

  • break

break语句会立刻结束整个控制流的执行。当你想要更早的结束一个switch代码块或者一个循环体时,你都可以使用break语句。

1.循环语句中的 break

当在一个循环体中使用break时,会立刻中断该循环体的执行,然后跳转到表示循环体结束的大括号(})后的第一行代码。不会再有本次循环迭代的代码被执行,也不会再有下次的循环迭代产生。

2.Switch 语句中的 break

当在一个switch代码块中使用break时,会立即中断该switch代码块的执行,并且跳转到表示switch代码块结束的大括号(})后的第一行代码

  • fallthrough

Swift 中的switch不会从上一个 case 分支落入到下一个 case 分支中。相反,只要第一个匹配到的 case 分支完成了它需要执行的语句,整个switch代码块完成了它的执行

如果你确实需要 C 风格的贯穿(fallthrough)的特性,你可以在每个需要该特性的 case 分支中使用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."
}
println(description)
// 输出 "The number 5 is a prime number, and also an integer."

注意:

fallthrough关键字不会检查它下一个将会落入执行的 case 中的匹配条件。fallthrough简单地使代码执行继续连接到下一个case 中的执行代码,这和 C 语言标准中的switch语句特性是一样的。

  • 带标签的语句(Labeled Statements)

在 Swift 中,你可以在循环体和switch代码块中嵌套循环体和switch代码块来创造复杂的控制流结构。然而,循环体和switch代码块两者都可以使用break语句来提前结束整个方法体。因此,显示地指明break语句想要终止的是哪个循环体或者switch代码块,会很有用。类似地,如果你有许多嵌套的循环体,显示指明continue语句想要影响哪一个循环体也会非常有用。

为了实现这个目的,你可以使用标签来标记一个循环体或者switch代码块,当使用break或者continue时,带上这个标签,可以控制该标签代表对象的中断或者执行。

产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个while循环体的语法,同样的规则适用于所有的循环体和switch代码块。

label name: while condition { statements }

下面的例子是在一个带有标签的while循环体中调用breakcontinue语句,该循环体是前面章节中蛇和梯子的改编版本。这次,游戏增加了一条额外的规则:
为了获胜,你必须刚好落在第 25 个方块中。
如果某次掷骰子使你的移动超出第 25 个方块,你必须重新掷骰子,直到你掷出的骰子数刚好使你能落在第 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
var square = 0
var diceRoll = 0

这个版本的游戏使用while循环体和switch方法块来实现游戏的逻辑。while循环体有一个标签名gameLoop,来表明它是蛇与梯子的主循环。

while循环体的条件判断语句是while square !=finalSquare,这表明你必须刚好落在方格25中。


gameLoop: while square != finalSquare {
if ++diceRoll == 7 { diceRoll = 1 }
switch square + diceRoll {
case finalSquare:
// 到达最后一个方块,游戏结束
break gameLoop
case let newSquare where newSquare > finalSquare:
// 超出最后一个方块,再掷一次骰子
continue gameLoop
default:
// 本次移动有效
square += diceRoll
square += board[square]
}
}
println("Game over!")

每次循环迭代开始时掷骰子。与之前玩家掷完骰子就立即移动不同,这里使用了switch来考虑每次移动可能产生的结果,从而决定玩家本次是否能够移动。

1.如果骰子数刚好使玩家移动到最终的方格里,游戏结束。break gameLoop语句跳转控制去执行while循环体后的第一行代码,游戏结束。
2.如果骰子数将会使玩家的移动超出最后的方格,那么这种移动是不合法的,玩家需要重新掷骰子。continue gameLoop语句结束本次while循环的迭代,开始下一次循环迭代。
3.在剩余的所有情况中,骰子数产生的都是合法的移动。玩家向前移动骰子数个方格,然后游戏逻辑再处理玩家当前是否处于蛇头或者梯子的底部。本次循环迭代结束,控制跳转到while循环体的条件判断语句处,再决定是否能够继续执行下次循环迭代。

注意:

如果上述的break语句没有使用gameLoop标签,那么它将会中断switch代码块而不是while循环体。使用gameLoop标签清晰的表明了break想要中断的是哪个代码块。 同时请注意,当调用continue gameLoop去跳转到下一次循环迭代时,这里使用gameLoop标签并不是严格必须的。因为在这个游戏中,只有一个循环体,所以continue语句会影响到哪个循环体是没有歧义的。然而,continue语句使用gameLoop标签也是没有危害的。这样做符合标签的使用规则,同时参照旁边的break gameLoop,能够使游戏的逻辑更加清晰和易于理解。

你可能感兴趣的:(控制流)