在 Go 中,for range 会根据 range 表达式中的值重复执行一段代码块。
range 表达式决定每次迭代会产生一个值还是两个值。
用range遍历切片,会按顺序产生每个元素的下标和值:
vals := []int{10, 20, 30}
for i, v := range vals {
fmt.Println(i, v)
}
// 0 10
// 1 20
// 2 30
用range遍历 map,会产生每个键和值,但迭代顺序无法保证。
Go 有意将 map 的迭代顺序随机化,因此同一个程序每次运行可能产生不同的输出。
hash := map[int]int{9: 10, 99: 20, 999: 30}
for k, v := range hash {
fmt.Println(k, v)
}
// 99 20
// 999 30
// 9 10
用range遍历字符串,会产生字节下标和每个 rune(Unicode 码点)。
在下面的例子中,子占三个字节,所以猫从字节下标3开始,而不是1。
for i, r := range "子猫" {
fmt.Println(i, r, string(r))
}
// 0 23376 子
// 3 29483 猫
如果你需要的是 rune 下标,就把字符串转换成 rune 切片:
for i, r := range []rune("子猫") {
fmt.Println(i, r, string(r))
}
// 0 23376 子
// 1 29483 猫
range可以直接遍历一个整数,产生的值从0开始,一直到该整数之前(不包含该整数)。
0 和负数都是合法的,但不会产生任何迭代。
for n := range 3 {
fmt.Println(n)
}
// 0
// 1
// 2
range接受类型为iter.Seq[V]或iter.Seq2[K, V]的迭代器。
迭代器是一个函数,每次产生序列中的一个值。
range随后就可以依次遍历这些值。
text := "The quick brown fox"
for word := range strings.FieldsSeq(text) {
fmt.Println(word)
}
// The
// quick
// brown
// fox
names := []string{"Alice", "Bob", "Vera"}
for i, v := range slices.All(names) {
fmt.Println(i, ":", v)
}
// 0 : Alice
// 1 : Bob
// 2 : Vera
最后,range可以接收通过 channel 发送的值,直到该 channel 在别处被关闭。
channel 会在后面的 track 教学大纲中更深入地讲解。
在 Go 中,如果一个变量声明了却从未使用,代码就无法编译。
vals := []int{10, 20, 30}
for i, v := range vals {
fmt.Println(v)
}
// Go build failed: declared and not used: i
如果只需要值,就把下标或键赋值给_来忽略它。
vals := []int{10, 20, 30}
for _, v := range vals {
fmt.Println(v)
}
// 10
// 20
// 30
如果只需要下标,就完全省略它:
vals := []int{10, 20, 30}
for i := range vals {
fmt.Println(i)
}
// 0
// 1
// 2
如果下标和值都不需要,两者都可以省略:
vals := []int{10, 20, 30}
count := 0
for range vals {
count++
}
// count == 3