意外片追加行为 [英] unexpected slice append behaviour

查看:164
本文介绍了意外片追加行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我今天在代码中遇到了奇怪的行为:当我在循环中追加元素 slice ,然后尝试根据循环结果创建新的切片,最后追加覆盖切片从前面的追加



在这个特殊的例子中,它表示 sliceFromLoop j g h 切片的最后一个元素不是 100 101 102 但是...总是 102



第二个示例 - sliceFromLiteral 的行为如预期。

 包主
$ b导入fmt

func create(iterations int)[] int {
a:= make([] int,0)
for i:= 0;我<迭代; i ++ {
a = append(a,i)
}
return a
}

func main(){
sliceFromLoop()
sliceFromLiteral()

}

func sliceFromLoop(){
fmt.Printf(**不按预期工作:** \ n\\ \\ n)
i:= create(11)
fmt.Println(initial slice:,i)
j:= append(i,100)
g:= append( i,101)
h:= append(i,102)
fmt.Printf(i:%v \ nj:%v\\'ng:%v\\\
h:%v\\\
,i,j,g,h)
}

func sliceFromLiteral(){
fmt.Printf(\\\
\\\
**按预期工作:* * \ n)
i:= [] int {0,1,2,3,4,5,6,7,8,9,10}
fmt.Println(initial slice: ,i)
j:= append(i,100)
g:= append(i,101)
h:= append(i,102)
fmt.Printf(i :%v \ nj:%v \\\
g:%v\\\
h:%v\\\
,i,j,g,h)
}

link to play.golang:
https://play.golang.org/p/INADVS3Ats



经过一些阅读,挖掘和试验后,我发现这个问题起源于 slices 引用相同的底层数组值,可以通过复制 slice 添加到新的,然后再添加任何东西,但它看起来相当......犹豫。

创建许多新切片的基本方法是什么在旧的和不担心改变旧切片的价值? 不要分配追加到除本身之外的其他任何东西上。



正如你在问题中所提到的,混淆是由于 append 都会更改基础数组并返回一个新切片(因为长度可能会更改)。你可以想象它会复制支持数组,但它不会,它只是分配一个指向它的新的 slice 对象。由于 i 永不改变,所有这些附加操作都会将 backingArray [12] 的值更改为不同的数字。



将它与 append 添加到一个数组,每次都会分配一个新的数组。
$ b

所以是的,你需要复制这个片段,然后才能使用它。

  func makeFromSlice(sl [] int)[] int {
result:= make([] int,len(sl))
copy(result,sl)
返回结果
}

func main(){
i:= make([] int,0)
for ii:= 0; II蛋白酶; 11; ii ++ {
i = append(i,ii)
}
j:= append(makeFromSlice(i),100)//正常工作
}




切片文字行为是由于新数组 ,如果append将超过支持数组的 cap 。这与分片文字无关,并且与内部超出上限的内容无关。

  a:= [ ] int {1,2,3,4,5,6,7} 
fmt.Printf(len(a)%d,cap(a)%d \\\
,a,len(a) ,cap(a))
// len(a)7,cap(a)7
$ bbb:= make([] int,0)
for i:= 1 ; i <8,i ++ {
b = append(b,i)
} // b:= [] int {1,2,3,4,5,6,7}
/ / len(b)7,cap(b)8

b = append(b,1)//任意数字,只要它达到上限

i:= append(b ,100)
j:= append(b,101)
k:= append(b,102)//这些工作如预期现在


I encountered weird behaviour in go code today: when I append elements to slice in loop and then try to create new slices based on the result of the loop, last append overrides slices from previous appends.

In this particular example it means that sliceFromLoop j,g and h slice's last element are not 100,101 and 102 respectively, but...always 102!

Second example - sliceFromLiteral behaves as expected.

package main

import "fmt"

func create(iterations int) []int {
    a := make([]int, 0)
    for i := 0; i < iterations; i++ {
        a = append(a, i)
    }
    return a
}

func main() {
    sliceFromLoop()
    sliceFromLiteral()

}

func sliceFromLoop() {
    fmt.Printf("** NOT working as expected: **\n\n")
    i := create(11)
    fmt.Println("initial slice: ", i)
    j := append(i, 100)
    g := append(i, 101)
    h := append(i, 102)
    fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}

func sliceFromLiteral() {
    fmt.Printf("\n\n** working as expected: **\n")
    i := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("initial slice: ", i)
    j := append(i, 100)
    g := append(i, 101)
    h := append(i, 102)
    fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}

link to play.golang: https://play.golang.org/p/INADVS3Ats

After some reading, digging and experimenting I found that this problem is originated in slices referencing the same underlaying array values and can be solved by copying slice to new one before appending anything, however it looks quite... hesitantly.

What's the idomatic way for creating many new slices based on old ones and not worrying about changing values of old slices?

解决方案

Don't assign append to anything other than itself.

As you mention in the question, the confusion is due to the fact that append both changes the underlying array and returns a new slice (since the length might be changed). You'd imagine that it copies that backing array, but it doesn't, it just allocates a new slice object that points at it. Since i never changes, all those appends keep changing the value of backingArray[12] to a different number.

Contrast this to appending to an array, which allocates a new literal array every time.

So yes, you need to copy the slice before you can work on it.

func makeFromSlice(sl []int) []int {
    result := make([]int, len(sl))
    copy(result, sl)
    return result
}

func main() {
    i := make([]int, 0)
    for ii:=0; ii<11; ii++ {
        i = append(i, ii)
    }
    j := append(makeFromSlice(i), 100)  // works fine
}


The slice literal behavior is explained because a new array is allocated if the append would exceed the cap of the backing array. This has nothing to do with slice literals and everything to do with the internals of how exceeding the cap works.

a := []int{1,2,3,4,5,6,7}
fmt.Printf("len(a) %d, cap(a) %d\n", a, len(a), cap(a))
// len(a) 7, cap(a) 7

b := make([]int, 0)
for i:=1; i<8, i++ {
    b = append(b, i)
}  // b := []int{1,2,3,4,5,6,7}
// len(b) 7, cap(b) 8

b = append(b, 1)  // any number, just so it hits cap

i := append(b, 100)
j := append(b, 101)
k := append(b, 102)  // these work as expected now

这篇关于意外片追加行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆