一个 C++ 程序员眼中的 Swift(三)

接下来的内容就更有趣了,方便的for-in、强大的switch、 穿越的break等等。每一个都戳到了 C++ 程序员的痛处或痒处。

  1. for-in

    现在大多数语言都已支持 for-in 语句了。C/C++ 还在固守成规。

    Swift 的 for-in 语句与它的区间运算符简直是绝配,写个循环如此简单:

    var sum = 0
    for i in 0..<10 {
    sum += i
    }
    

    迭代数组也极其方便:

    let names = ["xiaoming", "xiaolan", "xiaohong"]
    for i in 0..<names.count {
    println("\(names[i])")
    }
    

    迭代数组何必需要下标?

    let names = ["xiaoming", "xiaolan", "xiaohong"]
    for name in names {
    println("\(name")
    }
    

    往下看前先默念一遍 C++ 中如何迭代 std::map 的。然后再看 Swift 是怎么做的:

    let scores = ["xiaoming":98, "xiaolan":89, "xiaohong":99]
    for (name, score) in scores {
    println("\(names) got \(score)")
    }
    
  2. 增强的 switch

    1. 不再有漏写 break 的烦恼。Swift 的 switch 中每个 case 都是默认 break 的,无需在最后写 break

      但在 C/C++ 中,有时我们是故意不写 break 的。Swift 为了支持这一特性,增加了 fallthrough 语句。

      虽然增加了新的语句,但这更符合实际情况。在大多数情况下我们想要的是 break,而极少数情况下才需要 fallthrough 。而默认值应该是照顾大多数情况的。所以,Swift 中是默认 break 而需要显式写 fallthrougn ,而 C/C++ 中却是默认 fallthrough 而需要显式写 break

      下面是个例子:

      let skills = 2
      var description = "You are a "
      switch skills {
      case 2:
      description += "senior "
      fallthrough
      case 1:
      description += "expert"
      default:
      description += "good guy"
      }
      println(description)
      
    2. Swift 要求 switch 中的 case 必须是完备的,即列举所有的情况。当然,可以使用 default 来覆盖那些我们不关心的情况。

      Swift 还要求每个 case 下必须含有有效的语句(注释不算)。所以,如果你想忽略某个(些) case ,你需要显式的写个 break

    3. 每个 case 可以写多个条件:

      switch result {
      case "true", "True":
      println("correct")
      case "false", "False":
      println("wrong")
      default:
      println("invalid")
      }
      
    4. case 里的条件不只是一个或多个的值,还可以是区间:

      switch temperature {
      case -40 ... -10:
      println("frigid")
      case -10 ... 5:
      println("cold")
      case 5 ... 20:
      println("cool")
      case 20 ... 28:
      println("warm")
      case 28 ... 40:
      println("hot")
      default:
      println("not for human beings")
      }
      
    5. 还可以是元组:

      switch (strength, magic) {
      case (0, 0):
      println("die")
      case (0, _): // 下划线表示该项可为任意值。
      println("revive")
      case (80...100, 80...100)
      println("attack")
      case (0...30, _):
      println("escape")
      default:
      println("defense")
      }
      

      可以看到,各个条件是可以重叠甚至相同的。Swift 只执行第一个匹配到的条件下的语句。

    6. 值绑定

      有时你需要在 case 里的代码段里使用 case 所匹配到的值。这时可能就会用到值绑定。比如:

      let point = (0, 3)
      switch point {
      case (let row, 0):
      println("rown number of \(row)")
      case (0, let col):
      println("headline of \(col)")
      case let (row, col):
      println("data of (\(row), \(col))")
      }
      

      当然,也可以用 var 声明变量来进行值绑定。

      这个特性除了方便之外,似乎没有什么用。因为我们可以通过 point 这个常量来引用我们需要的值。比如:

      let point = (0, 3)
      switch point {
      case (_, 0):
      println("rown number of \(point.0)")
      case (0, _):
      println("headline of \(point.1)")
      default:
      println("data of (\(point.0), \(point.1))")
      }
      
    7. wherecase 加上条件判断。这让 switch 更像一系列 if...else 了。

      let point = (3,3)
      switch point {
      case (_, _) where point.0 > 0 && point.1 > 0:
      println("point is on the first quadrant")
      case let (x, y) where x > 0 && y < 0: // 用了值绑定,是不是方便多了。
      println("point is on the forth quadrant")
      case let (x, y) where x < 0 && y > 0:
      println("point is on the second quadrant")
      case let (x, y) where x < 0 && y < 0:
      println("point is on the third quadrant")
      default:
      println("point is on the axes")
      }
      
  3. 带标签的语句

    在嵌套循环中,有时我们想直接跳出到最外层循环。另外,在嵌套的 switch 里,或循环和 switch 混合嵌套的情况下,同样有这种需求。

    在 C/C++ 中可以使用 goto 语句来实现。有时这也不失为一种优雅的办法。在很多教材或文章中,甚至在很多老师和前辈的口中,goto 语句都被妖魔化了。为了避免使用 goto,宁可增加很多标志变量再绕几个弯儿来实现一句 goto 就可以实现的逻辑。即使有充分的理由在代码中使用了 goto,心理上也承受着会被别人嘲讽的压力。

    在 Swift 中,你完全可以抛掉这些心理负担了。因为只需在 forwhileswitch 等语句前加个标签,你就可以随心所欲的 breakcontinue 指定的控制流了。

    fl: for i in 0...20 {
    sl: switch i {
    case 0:
        fallthrough
    case 1:
        break sl
    default:
        for j in 2..<i {
            switch i % j {
            case 0:
                continue fl
            default:
                break
            }
        }
        println("\(i)")
    }
    }
    
  4. 未完待续

Creative Commons License Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution 4.0 International license .