Swift 控制语句
Control Flow指的是『逻辑控制语句』,包括if、if-else、for、for-in、while、do-while、switch等。其中大部分和OC中差不多,但Swift在某些方便增强了控制语句的功能。譬如for-in中引入..<
和...
操作符;switch语句更是大不一样了。本文也只针对for-in和switch语句展开阐述。
For-In
for-in语句基本上和OC中的for-in语句被太多区别,但新增了两个符号:..<
(类似于Python中的range()
)和...
:
..<
描述的区间是半闭区间(左闭右开),即0..<2
表示[0,2)
(0<=x<2,其中x为整数);...
描述的区间是全闭区间(左闭右闭),即0...2
表示[0,2]
(0<=x<=2,其中x为整数);
P.S:Swift刚开始推出来的时候,..<
的原型是..
,好在很快在后续版本中改成了..<
,否则实在是个灾难!看着一门语言一点点进步,最后发展成一门伟大的语言是一件挺棒的事情,Swift会发展成一门伟大的语言吗?我想会!
P.S:我个人觉得...
不是一个很好的设计,为什么要这么一个操作符呢?希望以后能找到答案吧!
Switch
Switch语句和if语句都属于条件语句
,switch会尝试把某个值与若干个模式(pattern)进行匹配。根据第一个匹配成功的模式,switch语句会执行对应的代码。当有可能的情况较多时,通常用switch语句替换if语句,如下:
switch someValueToConsider { case value1: // respond to value1 case value2, value3: // respond to value2 or value3 default: // otherwise, do something else }
switch语句都由多个case构成。为了匹配某些更特定的值,Swift提供了几种更复杂的匹配模式,这些模式将在本节的稍后部分提到。
Swift规定switch语句必须是「完备的」。这就是说,每一个可能的值都必须至少有一个case
块与之对应。在某些不可能涵盖所有值的情况下,你可以使用默认default
块满足该要求,这个默认块必须在switch语句的最后面。
不存在隐式fallthrough
在C和OC中,写switch语句时,一定要确保在每个case
语句的末尾添加一句break;
,这样才能确保switch代码块终止,否则会执行下一个case/default块,这种行为在Swift中被称为「fallthrough」。
与C语言和OC中的switch语句不同,在Swift中,当匹配的case
块中的代码执行完毕后,程序会自动终止switch语句,而不会继续执行下一个case
块。这也就是说,不需要在case
块中显式地使用break语句,这使得switch语句更安全、更易用,也避免了因忘记写break
语句而产生的错误。
除此之外,Swift还规定每一个case语句中都必须包含至少一条语句,像下面这样书写代码是无效的,因为第一个case块是空的:
let anotherCharacter: Character = "a" switch anotherCharacter { case "a": case "A": println("The letter A") default: println("Not the letter A") } // this will report a compile-time error
一个case也可以包含多个匹配模式,不同的pattern用逗号,
分开。
范围匹配
case
块的pattern可以是一个范围,如下:
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).") // prints "There are millions and millions of stars in the Milky Way."
元组
你可以使用元组在同一个switch
语句中测试多个值。元组中的元素可以是值,也可以是范围。另外,使用下划线_
来匹配所有可能的值,如下例子展示了如何使用(Int, Int)
类型的元组来分类下图中的点(x, y)
:
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") } // prints "(1, 1) is inside the box"
在上面的例子中,switch 语句会判断某个点是否是原点(0, 0),是否在红色的x轴上,是否在黄色y轴上,是否在一个以原点为中心的4x4的矩形里,或者在这个矩形外面。
不像C语言,Swift允许多个case匹配同一个值。实际上,在这个例子中,点(0, 0)可以匹配所有四个case。但是,如果存在多个匹配,那么只会执行第一个被匹配到的case块。考虑点(0, 0)会首先匹配case (0, 0)
,因此剩下的能够匹配(0, 0)的case块都会被忽视掉。
Value Bindings
在多个值匹配(使用tuple)时,我们常常会需要用到被检测值的局部值,switch语句还允许在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 an y value of \(y)") case (x, y): println("somewhere else at (\(x), \(y))") } // prints "on the x-axis with an x value of 2"
这个例子中,switch语句会判断某个点是否在x轴和y轴上,或者在别的地方。
Where语句
Swift中还有一个关键字where
,常用来处理更复杂的模式匹配;在case中使用where
关键字引导的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") }
这个例子中,switch语句会判断某个点是否在绿线(y=x)上,是否在紫线(y=-x)上,或者不在对角线上。
控制转移语句
控制转移代码能改变你代码的执行顺序,通过它你可以控制代码的跳转,在Swift中,有四种控制转移语句:
- continue
- break
- fallthrough
- return
除此之外,Swift还引入「labeled statements」,用来帮助更灵活的控制转移。
continue、break、return这几个控制语句与我们在C、OC中所了解的控制语句相比没啥不同,这里就不赘述了;所以本节内容包括:
- switch与break;
- switch与fallthrough;
- labeled statements;
switch与break
上文已经提到,在C和OC中,常常需要在case
块后面添加break;
代码,因为如果不这样,switch语句就紧接着执行后续的case语句(注意,后续的case pattern可不会被检查啊,而是直接执行后续case的body代码);但在Swift中,不需要这样,因为在Swift的switch语句中,当匹配的case块中的代码执行完毕后,程序会自动终止switch语句,而不会继续执行下一个case块。
所以按理说是不需要在switch语句中显式使用break指令的。但事实并非这样,因为Swift还有一个要求,规定每一个case语句中都必须包含至少一条语句。然而,在某些情况下的某些case分支里,可能真的啥都不想干,其他语言使用;
了事,但Swift语句中不需要使用;
,此时一般使用break语句代替,如下:
let numberSymbol: Character = "三" // Simplified Chinese for the number 3 var possibleIntegerValue: Int? switch numberSymbol { case "1", "١", "一", "๑": possibleIntegerValue = 1 case "2", "٢", "二", "๒": possibleIntegerValue = 2 case "3", "٣", "三", "๓": possibleIntegerValue = 3 case "4", "٤", "四", "๔": possibleIntegerValue = 4 default: break }
fallthrough
本节简单谈谈fallthrough,这可是新玩意儿。
上文已经多次提及,Swift语言中的switch不会从上一个case分支落入到下一个case分支中。相反,只要第一个匹配到的case分支完成了它需要执行的语句,整个switch代码块完成了它的执行。相比之下,C语言要求你显示的插入break语句到每个switch分支的末尾来阻止自动落入到下 一个case分支中。Swift语言的这种避免默认落入到下一个分支中的特性意味着它switch功能要比C语言的更加清晰和可预测,可以避免无意识地执行多个case分支从而引发的错误。
但有时候确实需要人为的fallthrough,此时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) // prints "The number 5 is a prime number, and also an integer."
值得一提的是,fallthrough关键字不会检查它下一个将会落入执行的case中的匹配条件。fallthrough简单地使代码执行继续连接到下一个case中的执行代码,这和C语言标准中的switch语句特性是一样的。
Labeled Statements
在Swift语言中,你可以在循环体(for循环或while循环)和switch代码块中嵌套循环体和switch代码块来创造复杂的控制流结构。然而,循环体和switch代码块两者都可以使用break 语句来提前结束整个代码块。因此,显示地指明break语句想要终止的是哪个循环体或者switch代码块会很有用。类似地,如果你有许多嵌套的循环体,显示指明continue语句想要影响哪一个循环体也会非常有用。
为了实现这个目的,你可以使用标签来标记一个循环体或者switch代码块,当使用break或者continue时,带上这个标签,可以控制该标签代表对象的中断或者执行。
产生一个带标签的语句是通过在该语句的关键词的同一行前面放置一个标签,并且该标签后面还需带着一个冒号。下面是一个while循环体的语法,同样的规则适用于所有的循环体和switch代码块:
labelName: while condition { // statements }
label的使用非常简单,如下有一段代码演示了label的应用:
gameLoop: while square != finalSquare { if ++diceRoll == 7 { diceRoll = 1 } switch square + diceRoll { case finalSquare: // diceRoll will move us to the final square, so the game is over break gameLoop case let newSquare where newSquare > finalSquare: // diceRoll will move us beyond the final square, so roll again continue gameLoop default: // this is a valid move, so find out its effect square += diceRoll square += board[square] } } println("Game over!")
显然,labeled statements
可以实现类似于C语言中goto
语句的功能。