为了账号安全,请及时绑定邮箱和手机立即绑定

golang范围表达式的奇怪行为

golang范围表达式的奇怪行为

Go
泛舟湖上清波郎朗 2021-07-01 14:05:07
我有这个测试代码,它只是从 int 切片中删除偶数:package mainimport "fmt"func main() {    a := []int{0, 1, 2, 3}    for i, v := range a {        fmt.Printf("i: %d v: %d\n", i, v)        fmt.Println("before", a)        if v%2 == 0 {            // delete a[i]            a = append(a[:i], a[i+1:]...)        }        fmt.Println("after", a, "\n")    }    fmt.Println("final", a)}输出是:i: 0 v: 0before [0 1 2 3]after [1 2 3] i: 1 v: 2before [1 2 3]after [1 3] i: 2 v: 3before [1 3]after [1 3] i: 3 v: 3before [1 3]after [1 3] final [1 3]你也可以在这里找到它http://play.golang.org/p/BFPxekBggS。我的问题是为什么变量v在最后两次迭代中计算为 3?提前致谢。
查看完整描述

2 回答

?
红颜莎娜

TA贡献1842条经验 获得超12个赞

在内部,切片就像一个包含三个元素的结构:

  • 一个支持数组

  • 后备数组的大小,可以访问为 cap(slice)

  • 切片的长度,可以访问为 len(slice)

在您的循环运行之前,支持数组 fora[0, 1, 2, 3]with cap(a) == len(a) == 4

当您a使用以下代码进行修改时:

a = append(a[:i], a[i+1:]...)

a由于新长度小于容量,因此新值共享原始的后备数组。因此,在第一次迭代中修改后,支持数组现在包含[1, 2, 3, 3]with len(a) == 3。数组中的最后一个元素通过正常的切片操作是不可见的,但保留其旧值。

在第二次迭代中,切片再次缩短,因此支持数组现在[1, 3, 3, 3]len(a) == 2

现在,当循环运行时,range表达式只计算一次,因此无论您在循环中进行什么更改,它始终会导致 4 次迭代。它还将从同一个支持数组返回结果,这解释了您看到的数字。


查看完整回答
反对 回复 2021-07-19
?
慕虎7371278

TA贡献1802条经验 获得超4个赞

问题是a您在迭代时正在修改(删除元素),因此结果可能有点......令人惊讶。我的猜测是,你的第一个删除后,a是在内存中类似的东西:[1 2 3 3],所以a[2]是3和你的第二个删除后,a是类似的东西在内存:[1 3 3 3]等a[3]是3。


因此,我的第一个建议是更改代码以使用传统for循环而不是范围,并且i仅在您不删除某些内容时才增加:


package main


import "fmt"


func main() {

    a := []int{0, 1, 2, 3}

    for i := 0; i < len(a); {

        v := a[i]

        fmt.Printf("i: %d v: %d\n", i, v)

        fmt.Println("before", a)

        if v%2 == 0 {

            // delete a[i]

            a = append(a[:i], a[i+1:]...)

        } else {

            i++

        }

        fmt.Println("after", a, "\n")

    }

    fmt.Println("final", a)


}

这是输出:


i: 0 v: 0

before [0 1 2 3]

after [1 2 3]


i: 0 v: 1

before [1 2 3]

after [1 2 3]


i: 1 v: 2

before [1 2 3]

after [1 3]                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               


i: 1 v: 3                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 

before [1 3]                                                                                                                                                                                                                                                                   

after [1 3]                                                                                                                                                                                                                                                                    


final [1 3]

我的第二个建议是反转循环(从末尾迭代)以避免递增/递减的“特殊情况”:


package main


import "fmt"


func main() {

    a := []int{0, 1, 2, 3}

    for i := len(a) - 1; i >= 0; i-- {

        v := a[i]

        fmt.Printf("i: %d v: %d\n", i, v)

        fmt.Println("before", a)

        if v%2 == 0 {

            // delete a[i]

            a = append(a[:i], a[i+1:]...)

        }

        fmt.Println("after", a, "\n")

    }

    fmt.Println("final", a)


}

这是输出:


i: 3 v: 3                                                                                                                                                                                                                                                                   

before [0 1 2 3]                                                                                                                                                                                                                                                               

after [0 1 2 3]                                                                                                                                                                                                                                                                 


i: 2 v: 2                                                                                                                                                                                                                                                                       

before [0 1 2 3]                                                                                                                                                                                                                                                               

after [0 1 3]                                                                                                                                                                                                                                                                   


i: 1 v: 1                                                                                                                                                                                                                                                                      

before [0 1 3]                                                                                                                                                                                                                                                                 

after [0 1 3]                                                                                                                                                                                                                                                                  


i: 0 v: 0                                                                                                                                                                                                                                                                      

before [0 1 3]                                                                                                                                                                                                                                                                 

after [1 3]                                                                                                                                                                                                                                                                    


final [1 3]


查看完整回答
反对 回复 2021-07-19
  • 2 回答
  • 0 关注
  • 212 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信