经典 C 1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> int main () { int i; int sum = 0 ; for (i = 0 ; i < 10 ; i++) { sum += i; } printf ("%d\n" , sum); }
Go
i 为循环变量,作用域仅限于 for 语句的隐式代码块范围内
1 2 3 4 5 6 7 8 9 package mainfunc main () { var sum int for i := 0 ; i < 10 ; i++ { sum += i } println (sum) }
多变量 1 2 3 4 5 6 7 8 9 package mainfunc main () { var sum int for i, j, k := 0 , 1 , 2 ; (i < 10 ) && (j < 20 ) && (k < 20 ); i, j, k = i+1 , j+5 , k+8 { sum += i + j + k } println (sum) }
省略
省略循环后置语句
1 2 3 for i := 0 ; i < 10 ; { i++ }
省略循环前置语句
1 2 3 i := 0 for ; i < 10 ; i++ {}
前后都省略,可以省略 ;
,仅保留循环判断条件表达式
1 2 3 4 i := 0 for i < 10 { i++ }
全部省略
for range
for range 针对不同的复合类型进行循环操作时,所声明的循环变量
的语义
是不一样
的
slice 1 2 3 4 sl := []int {1 , 2 , 3 , 4 , 5 } for i := 0 ; i < len (sl); i++ { println (sl[i]) }
语法糖:for range
1 2 3 4 sl := []int {1 , 2 , 3 , 4 , 5 } for i, v := range sl { println (i, v) }
不关心元素值
1 2 3 4 sl := []int {1 , 2 , 3 , 4 , 5 } for i, _ := range sl { println (i) }
1 2 3 4 sl := []int {1 , 2 , 3 , 4 , 5 } for i := range sl { println (i) }
不关心元素下标
1 2 3 4 sl := []int {1 , 2 , 3 , 4 , 5 } for _, v := range sl { println (v) }
都不关心
1 2 3 sl := []int {1 , 2 , 3 , 4 , 5 } for _, _ = range sl {}
1 2 3 4 sl := []int {1 , 2 , 3 , 4 , 5 } for range sl {}
string
for range 对于 string 来说,将采用字符视角
v
为 Unicode 码点
,即 rune
类型值,而非一个字节
i
为该 Unicode 码点
在内存编码(UTF-8
)的第一个字节
在字符串内存序列
中的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func main () { s := "中国人" for i, v := range s { fmt.Printf("%d %c %s %T,0x%x\n" , i, v, string (v), v, v) } }
builtin/builtin.go
map
for range 是循环 map 的唯一方法
1 2 3 4 5 6 7 8 9 m := map [string ]int { "one" : 1 , "two" : 2 , "three" : 3 , } for k, v := range m { println (k, v) }
channel
channel 是 Go 提供的并发原语
,用于多个 goroutine
之间的通信
1 2 3 4 c := make (chan int ) for v := range c { println (v) }
当 channel 中没有数据可读
的时候,for range 会阻塞
在对 channel 的读
操作上
直到 channel 关闭
,for range 才会结束
continue 1 2 3 4 5 6 7 8 9 sum := 0 sl := []int {1 , 2 , 3 , 4 , 5 , 6 } for _, v := range sl { if v%2 == 0 { continue } sum += v } println (sum)
label
标记
跳转的目标,常用于嵌套循环语句
,被用于跳转到外层循环
并继续执行外层循环语句的下一次迭代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainfunc main () { sum := 0 sl := []int {1 , 2 , 3 , 4 , 5 , 6 } loop: for _, v := range sl { if v%2 == 0 { continue loop } sum += v } println (sum) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport "fmt" func main () { sl := [][]int { {1 , 2 , 3 }, {4 , 5 , 6 , 5 }, {7 , 8 , 9 }, {5 }, } outerLoop: for i := 0 ; i < len (sl); i++ { for j := 0 ; j < len (sl[i]); j++ { if sl[i][j] == 5 { fmt.Printf("found 5 at (%d, %d)\n" , i, j) continue outerLoop } } } }
类 c 的实现方式,比较繁琐
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport "fmt" func main () { sl := [][]int { {1 , 2 , 3 }, {4 , 5 , 6 , 5 }, {7 , 8 , 9 }, {5 }, } for i := 0 ; i < len (sl); i++ { found := false for j := 0 ; j < len (sl[i]); j++ { if sl[i][j] == 5 { fmt.Printf("found 5 at (%d, %d)\n" , i, j) found = true break } } if found { continue } } }
goto
无限循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport "fmt" func main () { sl := [][]int { {1 , 2 , 3 }, {4 , 5 , 6 , 5 }, {7 , 8 , 9 }, {5 }, } outerLoop: for i := 0 ; i < len (sl); i++ { for j := 0 ; j < len (sl[i]); j++ { if sl[i][j] == 5 { fmt.Printf("found 5 at (%d, %d)\n" , i, j) goto outerLoop } } } }
不推荐使用 goto
,难以驾驭、可读性差、代码难以维护
break 1 2 3 4 5 6 7 8 9 10 11 sl := []int {5 , 19 , 6 , 3 , 8 , 12 } firstEven := -1 for _, v := range sl { if v%2 == 0 { firstEven = v break } } println (firstEven)
label
不带 label 的 break 仅能跳出其所在的最内层循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" func main () { sl := [][]int { {1 , 2 , 3 }, {4 , 5 , 6 , 5 }, {7 , 8 , 9 }, {5 }, } outerLoop: for i := 0 ; i < len (sl); i++ { for j := 0 ; j < len (sl[i]); j++ { if sl[i][j] == 5 { fmt.Printf("found 5 at (%d, %d)\n" , i, j) break outerLoop } } } }
几点注意 循环变量
循环变量在 for range 中仅会被声明1次
,且在每次迭代
中都会被重用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport ( "fmt" "time" ) func main () { m := []int {1 , 2 , 3 , 4 , 5 } for i, v := range m { go func () { time.Sleep(time.Second * 3 ) fmt.Println(i, v) }() } time.Sleep(time.Second * 10 ) }
等价转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport ( "fmt" "time" ) func main () { m := []int {1 , 2 , 3 , 4 , 5 } i, v := 0 , 0 for i, v = range m { go func () { time.Sleep(time.Second * 3 ) fmt.Println(i, v) }() } time.Sleep(time.Second * 10 ) }
为闭包函数
增加参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport ( "fmt" "time" ) func main () { m := []int {1 , 2 , 3 , 4 , 5 } for i, v := range m { go func (i, v int ) { time.Sleep(time.Second * 3 ) fmt.Println(i, v) }(i, v) } time.Sleep(time.Second * 10 ) }
副本
参与循环的是 range 表达式的副本
- 值拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { a := [5 ]int {1 , 2 , 3 , 4 , 5 } var r [5 ]int fmt.Println(a) for i, v := range a { if i < len (a)-1 { a[i+1 ] += 10 } r[i] = v } fmt.Println(a) fmt.Println(r) }
使用切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" func main () { a := [5 ]int {1 , 2 , 3 , 4 , 5 } var r [5 ]int fmt.Println(a) for i, v := range a[:] { if i < len (a)-1 { a[i+1 ] += 10 } r[i] = v } fmt.Println(a) fmt.Println(r) }
map
遍历 map
中的元素具有随机性
当 map 类型变量作为 range 表达式时,通过值拷贝
得到的 map 变量指向同一个
map
在循环的过程中,对 map 进行修改
,修改的结果不一定
会影响后续的迭代
删除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 m := map [string ]int { "tony" : 21 , "tom" : 22 , "jim" : 23 , } counter := 0 for k, v := range m { if counter == 0 { delete (m, "tony" ) } counter++ fmt.Println(k, v) } fmt.Println(counter)
当 tony 作为第 1 个迭代的元素
1 2 3 4 5 tony 21 tom 22 jim 23 3
否则
新增
为 map 新增元素,在后续的迭代中,可能出现,也可能不出现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport "fmt" func main () { m := map [string ]int { "tony" : 21 , "tom" : 22 , "jim" : 23 , } counter := 0 for k, v := range m { if counter == 0 { m["lucy" ] = 24 } counter++ fmt.Println(k, v) } fmt.Println(counter) }
出现
1 2 3 4 5 jim 23 lucy 24 tony 21 tom 22 4
不出现