注册 登录

清河洛

Go语言中的范围(Range)

qingheluo2021-09-04清河洛454
Go语言中range关键字用于for循环中迭代核心类型为数组、指向数组的指针、slice、string、map、允许接收操作的通道在1.22版本中增加了迭代整数的功能在1.23版本充增加了迭代特定签名函数的功能如果for语句中定于的变量为两个则返回元素的索引和索引对应的值如果for语句中定义的变量为一个则返回元素的索引遍历切片: nums := [] int {1,2,3,4,5} for index,val := range nums { fmt.Printf("索引 %d 的值为 %d ", index , val) } 遍历集合: maps := map [string] ...

Go语言中range关键字用于for循环中迭代核心类型为数组、指向数组的指针、slice、string、map、允许接收操作的通道

在1.22版本中增加了迭代整数的功能

在1.23版本充增加了迭代特定签名函数的功能

如果for语句中定于的变量为两个则返回元素的索引和索引对应的值

如果for语句中定义的变量为一个则返回元素的索引

遍历切片:

nums := [] int {1,2,3,4,5}
for index,val := range nums {
    fmt.Printf("索引 %d 的值为 %d ", index , val)
}

遍历集合:

maps := map [string] string {"name":"三千","age":"33","sex":"男"}
for index,val := range maps {
    fmt.Printf("索引%s的值为%s", index , val)
}

遍历字符串:

str := "hello"
for index,val := range str {
    fmt.Prinft("索引%s的值为%s", index , val)
}
其中val为当前索引的字符对应的int32的Unicode值

如果字符串中有中文,则该中文字符之后的一个字符索引将增加3

str := "华夏"
for index,val := range str {
    fmt.Printf("索引%d的值为%d", index , val)
}
索引0的值为21326
索引3的值为22799

遍历数字:

for i := range 10 {
    fmt.Println(i)
    // 输出0 1 2 3 4 5 6 7 8 9
}

当遍历数字时,仅可由一个变量接收,如果两个变量接收会报错

无法确定仅接收索引或仅接收值,因为索引和值相等

for i,v : = range 10 {} // 会报错

读取通道:

for val := range ch{
    fmt.Println("接收到的通道值:", val)
    // 当通道关闭后循环结束
}

在1.22之前的版本中,for...range循环时的迭代变量只创建一次,在每次迭代中为这个变量赋予新的值,由于这个问题会出现一些意想不到的结果

demo := []int{1, 2, 3, 4, 5}

for _,v := range demo {
    go func() {
        fmt.Println(v)
    }()
}
time.Sleep(time.Second)

上面代码我们预想的应该是一个协程输出1,一个协程输出2,...,一个协程输出5,但实际上却是不可预知的

由于携程函数中的变量访问是在执行打印函数时才会访问,在创建协程时并不会访问,如当迭代为3时,所有在此时刚好执行打印函数的携程都会输出3,在迭代完成后所有的协程都会输出5

针对这个问题,之前的解决办法是在携程函数中使用一个局部变量,在创建协程时将此时迭代的值传递给它,这样就可以保证每次迭代都能访问到正确的值

for _,v := range demo {
    go func() {
        newV := v
        fmt.Println(newV)
    }()
}
time.Sleep(time.Second)
或者
for _,v := range demo {
    go func(v int) {
        fmt.Println(v)
    }(v)
}
time.Sleep(time.Second)

这样在创建协程时就访问并传递了此时迭代的值,保证每次迭代都能访问到正确的值

在1.22版本中,官方已经修复了这个问题,现在for...range循环的迭代变量在每次迭代中都重新创建,不会出现上述问题



网址导航