我有这个测试代码,只删除int切片中的偶数:
package main
import "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: 0
before [0 1 2 3]
after [1 2 3]
i: 1 v: 2
before [1 2 3]
after [1 3]
i: 2 v: 3
before [1 3]
after [1 3]
i: 3 v: 3
before [1 3]
after [1 3]
final [1 3]
您也可以在http://play.golang.org/p/BFPxekBggS找到它。我的问题是为什么变量v
在最后两次迭代中的计算结果为3?提前谢谢。
答案 0 :(得分:5)
在内部,切片就像一个包含三个元素的结构:
cap(slice)
len(slice)
在循环运行之前,a
的支持数组为[0, 1, 2, 3]
cap(a) == len(a) == 4
。
使用以下代码修改a
时:
a = append(a[:i], a[i+1:]...)
a
的新值共享原始的后备数组,因为新长度小于容量。因此,在第一次迭代中进行修改后,后备阵列现在包含[1, 2, 3, 3]
len(a) == 3
。通过普通切片操作看不到数组中的最后一个元素,但保留其旧值。
在第二次迭代中,切片再次缩短,因此支持数组现在为[1, 3, 3, 3]
len(a) == 2
。
现在当循环运行时,range
表达式只被评估一次,因此无论你在循环中做出什么改变,它总是会导致4次迭代。它也将返回来自相同后备阵列的结果,这解释了您所看到的数字。
答案 1 :(得分:4)
问题是你在迭代时正在修改a
(删除元素),所以结果可能有点......令人惊讶。我的猜测是,在您第一次删除后,a
就像记忆中的那样:[1 2 3 3]
,因此a[2]
是3
,并且在您第二次删除后{{1}在内存中就是这样的: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)
}