Looping with For Statements
Go有两种for语句,纯for语句和for ... range语句。以下是它们的语法:
for { // Infinite loop
block
}
for booleanExpression { // While loop
block
}
for optionalPreStatement; booleanExpression; optionalPostStatement { ➊
block
}
for index, char := range aString { // String per character iteration ➋
block
}
for index := range aString { // String per character iteration ➌
block // char, size := utf8.DecodeRuneInString(aString[index:])
}
for index, item := range anArrayOrSlice { // Array or slice iteration ➍
block
}
for index := range anArrayOrSlice { // Array or slice iteration ➎
block // item := anArrayOrSlice[index]
}
for key, value := range aMap { // Map iteration ➏
block
}
for key := range aMap { // Map iteration ➐
block // value := aMap[key]
}
for item := range aChannel { // Channel iteration
block
}
花括号是必须要的,但是分号仅在有option pre或者post-statement时,才需要(➊); 如果一个变量在optional statement中创建,或者捕获由range子句产生的值(e.g., 使用了 := 操作符), 它们的作用域从声明的位置到整个for语句的结束。
纯for语句中的Boolean expression必须是bool类型,因为Go不会自动对非布尔转换为布尔类型。
第二个遍历字符串的for ... range(➌), 获得每个字节的位置。对于7位的ASCII字符串s, 比如s为"XabYcZ", 它将产生的索引为0,1,2,3,4,5. 但对于UTF-8字符串s,比如"XαβYγZ",它的索引为0,1,3,4,5,8. 第一个遍历字符串的for ... range(➋)通常比第二个方便(➌)
第二个遍历数组或slice的for ... range(➎), 将生成每个元素的索引,范围从0到len(slice)-1. 这种语法和第一种遍历数组和slice的for ... range语法(➍) 经常被使用,正因为有这两种语法,也导至了纯for循环的使用。
遍历map的for ... range(➏), 将返回元素的key和value, 但元素出现的顺序是任意的。如果需要以顺序的方式输出,一种解决方法是使用第二种语法(➐), 将key添加到一个slice,然后对这个slice进行排序——我们之前在讲map时,讲到过这种方法。另一种解决方案是 使用排序的数据结构——比如ordered map. 我们将在下一章中例子中讲到。
如果任意一种语法(➋–➐)用于一个空的字符串,数组,slice,map。for循环将什么也不做,控制权交给下一条语句。
一个for循环可以通过break语句,在任意时间打断。控制权交给for loop后面的那条语句,或者——如果break语句指定了一个标签,则跳到指定标签的for,switch, or select语句之后。如果需要返回到for循环的conditions(纯for)或者range子句的下一次遍历,可以使用contiune语句。
假如我们有一个二维的slices(e.g., [][]int), 我们要搜索一个指定的值。这里我们有两种方法。都是使用第二种遍历数组或slice的语法。
found := false
for row := range table {
for column := range table[row] {
if table[row][column] == x {
found = true
break
}
}
if found {
break
}
}
found := false
FOUND:
for row := range table {
for column := range table[row] {
if table[row][column] == x {
found = true
break FOUND
}
}
}
一个标签的定义是一个标识符后面跟一个冒号。两段代码都实现相同的一件事情,下面那段更短也更清晰,可果找到了,则跟出最外层的循环。
标签可用于for循环,switch语句和select语句。break和continue都可以指定标准和用于for循环内部。而break也可以用于switch和select语句。
Label也可以作为语句出现,作为goto语句的目标(goto label). 如果一个goto语句跳过了创建变量的语句,那么将发生什么都是未知的——如果我们够幸运,程序直接不能运行。但有可能程序继续运行,并且产生假的结果。使用goto语句的一种情况是当需要自动产生代码时。