如何确保切片使用另一个切片的副本而不是对它的引用?

时间:2015-10-06 01:47:57

标签: go slice

我正在学习GO,我有理论上的问题。

如何使用切片副本而不是对它的引用?

package main

import "fmt"

func main() {
    // slice containing 3 items
    slice1 := []int{1, 2, 3}
    // make an empty slice
    slice2 := make([]int, 2, 5)
    // create slice3 by appending int 4 to slice2
    slice3 := append(slice2, 4)
    // print [0 0 4]
    fmt.Println(slice3)
    // copy elements of slice1 onto slice2
    copy(slice2, slice1)
    // print [1 2 3] [1 2] [1 2 4]; how to make sure slice3 is using a copy [0 0 4]?
    fmt.Println(slice1, slice2, slice3)
}

problem playground link

我想出了一个潜在的解决方案,但它没有意义,因为它依赖于slice3被创建为空,slice2被copy()复制到slice3上。没有捷径吗?

package main

import "fmt"

func main() {
    // slice containing 3 items
    slice1 := []int{1, 2, 3}
    // make an empty slice
    slice2 := make([]int, 2, 5)
    // create slice3, copy slice2 and append int 4 to slice3
    slice3 := make([]int, 2)
    copy(slice3, slice2)
    slice3 = append(slice3, 4)
    // print [0 0 4]
    fmt.Println(slice3)
    // copy elements of slice1 onto slice2
    copy(slice2, slice1)
    // print [1 2 3] [1 2] [0 0 4];
    fmt.Println(slice1, slice2, slice3)
}

solution playground link

修改

read有一种奇特的行为,在这个天真的例子中可以作为解决方案(见下文)。但是,在任何其他情况下,它都行不通。 基本上,如果创建的空切片没有指定基础数组的大小,GO的append函数会提供该数组的副本,否则,如果有增长空间,append将返回引用原始数组的切片。

注意:唯一的变化是slice2 := make([]int, 2, 5)slice2 := make([]int, 2)

package main

import "fmt"

func main() {
    // slice containing 3 items
    slice1 := []int{1, 2, 3}
    // make an empty slice
    slice2 := make([]int, 2)
    // create slice3 by appending int 4 to slice2
    slice3 := append(slice2, 4)
    // print [0 0 4]
    fmt.Println(slice3)
    // copy elements of slice1 onto slice2
    copy(slice2, slice1)
    // print [1 2 3] [1 2] [1 2 4]; how to make sure slice3 is using a copy [0 0 4]?
    fmt.Println(slice1, slice2, slice3)
}

playground with a wanted behaviour

所以问题就变成了:当我们追加的切片指向具有指定大小和空间的数组时,是否有可能复制上述行为?

编辑2: 我认为对于我想要达到的目标存在一些困惑。 如何在第一次调用中使用的格式传递切片时获得第二个调用的结果?

package main

import "fmt"

func main() {
    fmt.Println("s3 references an array of s1")
    worker(make([]int, 2, 5))
    fmt.Println("\ns3 copies an array of s1")
    worker(make([]int, 2))
}

func worker(s1 []int) {
    s2 := []int{1, 2, 3}
    fmt.Println(s1)
    s3 := append(s1, 4)
    fmt.Println(s3)
    copy(s1, s2)
    fmt.Println(s3)
}

playground

1 个答案:

答案 0 :(得分:0)

有些人评论说我昨晚不够清楚。所以我想在@CoreyOgburn和@JimB的帮助下澄清并提供答案

我正在学习关于GO的切片,我发现了一个不一致的地方,这让我相信我做错了什么。虽然这不是一个真实的例子,但我发现以下是复制和追加功能的一个很好的例子。

package main

import "fmt"

func main() {
    fmt.Println("s3 references an array of s1")
// we pass a slice of length 2 and capacity 5
    worker(make([]int, 2, 5))
    fmt.Println("\ns3 copies an array of s1")
// we pass a slice of lenght 2 and capacity 2
    worker(make([]int, 2))
}

func worker(s1 []int) {
// create new slice for future use
    s2 := []int{1, 2, 3}
    fmt.Println(s1)
// create a new slice by appending a value to a slice passed into this function 
    s3 := append(s1, 4)
// s3 holds whatever was passed into this function + int 4, that we just appended
    fmt.Println(s3)
// copy contents of s2 onto s1
    copy(s1, s2)
// if s1 had spare capacity when it was passed i.e. make([]int, 2, 5) s3 will be referencing the same array as s1, hence s3 will now hold the same values as s1
// if s1 capacity was the same as its length i.e. make([]int, 2) s3 will be referencing a new array after append(), hence copy has no effect on the values of s3
    fmt.Println(s3)
} 

@JimB发布了一条评论,其中包含blog post explaining how slices work的链接,如果您正在学习该语言,这是一个很好的阅读。 A possible "gotcha"部分中最重要的是对“修复”的解释。对于现实生活场景,可以推断为修复我的示例中的不一致。 (创建传递切片的副本并使用它)
Playground