美文网首页
循环 & 提前退出

循环 & 提前退出

作者: 曹来东 | 来源:发表于2018-09-29 15:57 被阅读6次

    for-in循环

    从Swift 3.0版本起,for 循环语句只能使用 for-in 的形式。该形式的语法如下:
    for obj in sequence {}
    这里,obj 是一个常量,其类型为 sequence 中的元素类型。而 sequence 则是一个遵循 Sequence 协议的类型对象。当然,这里的 obj 也可以是一个元组。
    我们之前谈到的所有收集类型,包括表示范围类型的 CountableRange 与 CountableClosedRange 都遵循了 Sequence 协议,因此都可以对它们的对象实例做 for-in 循环迭代。我们下面举一个简单的例子来说明如何计算1到10000的总和。

    var sum = 0
     
    for i in 1 ... 10000 {
    sum += i
    }
     
    // 输出:sum = 50005000
    print("sum = \(sum)")
    

    上面的代码非常简单,for-in 循环中的常量 i 在每次迭代的开头就获取 in 后面对象的当前元素值,然后该对象所包含的迭代器自动指向下一个元素。所以每次执行 sum += i 语句时,sum 所加的都是 i 从范围对象中所获得的当前迭代的元素值。
    为了代码编写方便,CountableRange 类型与 CountableClosedRange 类型还都具有 reversed() 实例方法,可以将当前范围元素进行倒序排序。我们看以下例子

    // 这里将输出i = 9到i = 1
    for i in (1 ..< 10).reversed() {
    print("i = \(i)")
    }
    

    这里,(1 ..< 10) 的原本第一个元素是1,最后一个元素是9。倒序编排之后,第一个元素就变成了9,最后一个元素就变成了1。所以这里会打印 i = 9 一直到 i = 1。
    如果我们就想单纯循环若干次打印一些语句啥的,那么 for 和 in 之间可以用一个通配符作为占位符,表示对象缺省。比如以下代码:

    // 这里仅仅打印三行Hello, world
    for _ in 0 ..< 3 {
    print("Hello, world")
    }
    

    下面我们来谈谈对收集类型元素的迭代。收集类型的对象可直接作为 in 后面的表达式,在循环体内所访问的 for 后面的常量对象就是收集类型对象当前迭代访问的元素。由于 Array 类型与 Set 类型比较相似,所以我们先放在一起说。下面举的例子是依次打印一个 Array 对象与 Set 对象中的所有元素的值。

    let array = [1, 2, 3, 4]
     
    // 依次打印array数组对象中的所有元素值
    for elem in array {
    print("elem = \(elem)")
    }
     
    let set: Set<Int> = [1, 2, 3, 4]
     
    // 依次打印set集合对象中的所有元素值
    for elem in set {
    print("elem = \(elem)")
    }
    

    我们看到,使用 for-in 循环去迭代数组与集合中的所有元素非常方便。不过有时我们除了想要获得数组和集合当前迭代的元素之外,还想要获取当前元素对应的索引值。我们当然可以在循环外声明一个局部整数对象作为计数器,然后在迭代中做加1处理。不过,数组以及集合等收集类型自身包含了一个 enumerated() 实例方法,我们可以用它同时获取当前收集类型对象的元素值以及索引值。此时,for 后面的常量就是一个元组常量,该元组的第一个元素存放的是索引值,第二个元素存放的是元素值。我们看以下代码例子:

    let array = [1, 2, 3, 4]
     
    // 依次打印array数组对象中的所有元素值,
    // 以及当前元素对应的索引值
    for (index, elem) in array.enumerated() {
    print("index = \(index), elem = \(elem)")
    }
     
    let set: Set<Int> = [1, 2, 3, 4]
     
    // 依次打印set集合对象中的所有元素值,
    // 以及当前元素对应的索引值
    for (index, elem) in set.enumerated() {
    print("index = \(index), elem = \(elem)")
    }
    

    下面介绍使用 for-in 循环列出一个字典对象中的所有键与值。我们知道,一个字典的元素是由一个键和它对应的一个值构成,所以枚举出字典中每个元素的时候,我们也需要借助元组对象来分别存放字典元素中的键和值。我们先看以下例子:

    let dict = ["k1":1, "k2":2, "k3":3]
    // 依次打印dict中的每个键以及它所对应的值
    for (key, value) in dict {
    print("key = \(key), value = \(value)")
    }
    

    此外,字典对象也有 enumerated() 实例方法,尽管用途不大。不过像 [String : Int] 字典类型的 enumerated() 实例方法所返回的类型为:(offset: Int, element: (key: String, value: Int))。也就是说,元组类型包含了相应的标签,因此我们在写该元组字面量的时候需要把每个标签都加上。下面我们来看例子:

    let dict = ["k1":1, "k2":2, "k3":3]
     
    // 依次打印dict中每个元素对应的索引值,
    // 以及每个元素的键以及它所对应的值
    for (offset: index, element: (key: key, value: value)) in dict.enumerated() {
    print("index = \(index), key = \(key), value = \(value)")
    }
    

    下面谈谈在 for-in 循环中使用 break 语句和 continue 语句的情况。break 语句用在 for-in 循环中与用在 switch 语句块中的功能类似,表示跳出当前循环体。 continue 语句用于 for-in 循环中表示直接跳过其下面的 for-in 循环体中的语句,直接执行 for-in 循环的下一次迭代。这两条语句都属于控制传输语句(Control Transfer Statements),并且仅作用于当前的循环体。因为 for-in 循环是可嵌套的,所以如果当 break 语句用于内部嵌套的 for-in 循环体中,那么跳出的只是内部的 for-in 循环体。我们下面举一些例子进行说明:

    let array = [1, 2, 3]
     
    for elem in array {
    // 这里嵌套一个for循环
    for i in 0 ... elem {
    // 当i等于2时跳出内部for-in循环
    if i == 2 {
    break
    }
     
    // 这条语句每当i == 2成立时就会被跳过
    print("i = \(i)")
    }
     
    // 这条语句每次做array的迭代时都会执行
    print("elem = \(elem)")
    }
     
    for elem in array {
     
    // 这里当elem的元素值为2时就跳过当前迭代的剩余语句,
    // 直接跳到循环开始,做下一次迭代
    if elem == 2 {
    continue
    }
     
    // 因此这里的打印只会打印出1和3的值
    print("elem = \(elem)")
    }
    

    while循环

    Swift编程语言中的 while 循环与C语言的类似。其语法形式为:

    while condition {
    // 一条或多条执行语句
    }
    

    大家这里需要注意的是,condition 是一个布尔类型的表达式,与C语言有所不同的是,condition 不需要用圆括号围起来,当然使用圆括号也没有问题。其次, condition 后面的花括号不能省,哪怕循环体中就只有一条语句,这一点与C语言也是有所区别的。当然,就可读性上而言,使用花括号确实会更易读一些。
    执行 while 循环的逻辑非常简单。先计算 condition 表达式,如果结果为 true 则执行循环体内的语句,否则跳出循环体,执行 } 后的语句。每次迭代执行完循环体中的所有语句之后,再回到 condition 表达式进行判断,以此反复执行。
    我们下面举一些简单例子来看看 while 循环的具体使用。

    // 计算1到10000的求和
    var sum = 0
    var number = 1
     
    while number <= 10000 {
    sum += number
    number += 1
    }
     
    print("sum = \(sum), number = \(number)")
     
    // 计算10的阶乘
    number = 10
    var factorialResult = 1
     
    while number > 0 {
    factorialResult *= number
    number -= 1
    }
     
    print("The factorial of ten is: \(factorialResult)")
     
    // 计算40级的斐波那契数列
    var old = 0, new = 1
    number = 1
     
    while number < 40 {
    let tmp = new
    new += old
    old = tmp
    number += 1
    }
     
    print("Fibonacci result: \(new)")
    

    while 循环中使用 break 语句和 continue 语句的情况与 for-in 循环相似。 break 语句用于跳出当前循环体;而 continue 语句则是跳过当前循环体中其以下的执行语句,直接回到 condition 表达式进行判断,准备做下一次迭代。while 循环与 for-in 循环一样,可以进行嵌套,而且这两种形式的循环还可以相互嵌套。我们看以下代码例子:

    var i = 3;
     
    while i > 0 {
    var j = 4;
     
    while j > 0 {
    print("i = \(i), j = \(j)")
    j -= 1
    if i + j < 3 {
    break
    }
    }
     
    i -= 1
    if i == 2 {
    continue
    }
     
    print("Outside i = \(i)")
    }
     
    以上代码输出结果为:
    i = 3, j = 4
    i = 3, j = 3
    i = 3, j = 2
    i = 3, j = 1
    i = 2, j = 4
    i = 2, j = 3
    i = 2, j = 2
    i = 2, j = 1
    Outside i = 1
    i = 1, j = 4
    i = 1, j = 3
    i = 1, j = 2
    Outside i = 0
     
    

    repeat-while循环

    Swift编程语言中的 repeat-while 循环与C语言中的 do-while 循环类似,只不过由于Swift中的关键字 do 作为引出语句块使用,所以新引入了关键字 repeat 以暗示由它所引出的语句块作为一个循环体使用。repeat-while 循环的语法形式为:

    repeat {
    // 循环体中的一条或多条执行语句
    }
    while condition
    

    其执行逻辑为:先执行 repeat 语句块中的语句,然后判断 condition 表达式的值,如果为 true,那么跳转到 repeat 语句块的第一条语句处继续做循环迭代,否则跳出当前循环体。下面举一些与上面的例子相仿的代码:

    // 计算1到10000的求和
    var sum = 0
    var number = 0
     
    repeat {
    number += 1
    sum += number
    }
    while number < 10000
     
    print("sum = \(sum), number = \(number)")
     
    // 计算10的阶乘
    number = 10
    var factorialResult = 1
     
    repeat {
    factorialResult *= number
    number -= 1
    }
    while number > 0
     
    print("The factorial of ten is: \(factorialResult)")
     
    // 计算第40项的斐波那契数列
    var old = 0, new = 1
    number = 1
     
    repeat {
    let tmp = old
    old = new
    new += tmp
    number += 1
    }
    while number < 40
     
    print("Fibonacci result: \(new)")
    

    这能看出 while 循环与 do-while 循环之间的差异。
    repeat-while 循环中的 break 语句以及 continue 语句与 while 循环中的也类似。只不过这里的 continue 语句不是直接跳转到 repeat 的开头,而是跳转到 while 后面 condition 表达式执行判断。

    var count = 5
     
    func foo() -> Bool {
    count -= 1
    print("count = \(count)")
    return count > 0
    }
     
    repeat {
     
    if count == 3 {
    continue
    }
     
    print("This is a loop!")
    }
    while foo()
     
    上述代码的输出如下:
    This is a loop!
    count = 4
    This is a loop!
    count = 3
    count = 2
    This is a loop!
    count = 1
    This is a loop!
    count = 0
    

    标签语句

    现代大部分高级编程语言都不含有 goto 语句,Swift也不例外,不过Swift编程语言具有标签语句,可以方便地跳出指定的 do 语句块、if 语句块、switch 语句块、 for-in 语句块、while 语句块或 repeat 语句块。当我们要跳出某个指定的语句块时,只需要在上述语句块之前添加自己指定的标签,然后通过 break label 语句进行跳出。在循环体中,标签也可以与 continue 一起使用以跳过某个指定循环的当前迭代,直接执行下一次迭代。
    这里Swift比较神奇的是,break label 语句可以放在 do 语句块以及 if 语句块中,但是这些语句块一单使用了 break,则后面必须添加标签,否则编译器就会报语法错误。
    我们下面先看 break label 语句的使用情况:

    var a = 100
     
    // 在do语句块前添加标签label
    label: do {
    // 在if语句块前添加标签label2
    label2: if a > 50 {
    // 跳出do语句块
    break label
    }
    else {
    // 由于else前面以及else if前面不能添加标签,
    // 所以遇到这种情况时,我们单独写一个else块,
    // 然后在里面添加if语句块
    label3: if a < 100 {
    if a < 50 {
    // 跳出label3语句块
    break label3
    }
    else {
    // 跳出label2语句块
    break label2
    }
    }
    }
     
    // 直接跳出do语句块
    break label
    }
     
    // 在for-in语句块前添加标签
    for_label: for i in 0 ..< a {
    // 在switch语句块前添加标签
    switch_label: switch i {
    case 10 ... 20:
    let tmp = a * a
    if tmp < 100 {
    // 直接跳出switch语句块
    break switch_label
    }
    case 30 ... 100:
    if i > 60 {
    // 直接跳出for语句块
    break for_label
    }
     
    default:
    break
    }
     
    var j = i + 1
    // 在while语句块前添加标签
    while_label: while j > 0 {
    if j > 50 {
    // 直接跳出for语句块
    break for_label
    }
    else if j > 20 {
    // 跳出while语句块
    break while_label
    }
    j -= 1
    }
     
    j = i + 1
    // 在repeat语句块前添加标签
    repeat_label: repeat {
    if j > 50 {
    // 直接跳出for循环
    break for_label
    }
    else if j > 30 {
    // 跳出repeat语句块
    break repeat_label
    }
    j -= 1
    }
    while j > 0
    
    

    下面再介绍 continue label 语句的使用。

    let array = [1, 2, 3]
     
    for_label: for i in array {
    var j = 0
     
    while j < i {
    print("j = \(j)")
    // 当i等于2时将跳过后面的所有执行,
    // 直接跳转到for循环做下一次迭代
    if i == 2 {
    continue for_label
    }
     
    j += 1
    }
     
    print("i = \(i)")
    }
     
    var count = 3
     
    func decrease() -> Bool {
    count -= 1
    return count > 0
    }
     
    repeat_label: repeat {
     
    for i in 0 ..< count {
    print("i = \(i)")
     
    // 当count值为2的时候则直接跳过后面的所有执行,
    // 跳转到repeat-while循环语句块的while条件表达式处进行执行
    if count == 2 {
    continue repeat_label
    }
    }
     
    print("count = \(count)")
    }
    while decrease()
    

    提前退出

    Swift编程语言引入了 guard 语句,使得当 guard 后面的布尔表达式为假时,执行跳出当前语句块的行为。所以这一语法特征又被称为“提前退出”(Early Exit)。提前退出的语法形式为:

    guard condition else {
    // 跳出当前语句块的执行语句
    }
    

    这里,else 语句块中也可包含多条语句,但最终必须以return、break、 continue、throw,或调用非返回函数(比如exit())作为退出语句执行。 guard 语句其实更多地是以条件绑定的形式使用,这将会在后面介绍Optional的时候做详细描述。下面先举一些例子来看一看 guard 语句的基本使用。

    var number = 10
     
    guard number < 100 else {
    // 这里使用exit()函数退出
    exit(0)
    }
     
    if_label: if number > 5 {
    let a = number * number
    guard a > 90 else {
    // 这里使用break语句跳出
    break if_label
    }
    }
     
    while number > 0 {
     
    let a = number
    number -= 1
     
    // 这里如果a为5的话,
    // 则直接跳过下面的打印语句,
    // 做下一次循环迭代
    guard a != 5 else {
    continue
    }
     
    print("number = \(a)")
    }
    

    我们这里可以看到, guard 语句类似于一个断言,即确保其后面的布尔表达式为真,否则就执行紧跟在其后的 else 语句块中的语句。以上代码例子中,guard 与 if 功能类似,还体现不出其强大的效果,后面讲条件绑定的时候就能看到Swift编程语言引入这个语法特征的真正目的了。

    相关文章

      网友评论

          本文标题:循环 & 提前退出

          本文链接:https://www.haomeiwen.com/subject/ufdvoftx.html