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编程语言引入这个语法特征的真正目的了。
网友评论