go - 如何确保 slice 使用的是另一个 slice 的副本而不是对它的引用?

标签 go slice

我正在学习 GO,但我有一个理论问题。

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

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 创建为空并通过 copy() 将 slice2 复制到 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在这个天真的例子中有一种特殊的行为可以作为解决方案(见下文)。但是,在任何其他情况下它都行不通。 基本上,如果在没有指定底层数组大小的情况下创建空 slice ,GO 的 append 函数会提供该数组的副本,否则,如果有增长空间,append 将返回引用原始数组的 slice 。

注意:唯一的变化是 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

所以问题变成了:当我们附加到的 slice 指向具有指定大小和增长空间的数组时,是否可以复制上述行为?

编辑 2: 我认为我想要实现的目标有些困惑。 如何在以第一次调用中使用的格式传递 slice 时获取第二次调用的结果?

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

最佳答案

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

我在学习 GO 中的 slice 时发现了一个不一致的地方,这让我相信我做错了什么。虽然这不是现实生活中的例子,但我发现以下是复制和附加功能的一个很好的例子。

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" 部分中最重要的是对现实生活场景的“修复”的解释,可以推断为修复我的示例中的不一致。 (创建传递的 slice 的副本并改用它)
Playground

关于go - 如何确保 slice 使用的是另一个 slice 的副本而不是对它的引用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32960461/

相关文章:

python - 每第 n 行 Pandas iloc 复杂切片

go - 另一个 golang 提出了关于理解它如何处理的问题

go - 无法杀死Go Server

python - 如何在 python 中从 ndarray 中选择 n 项并跳过 m?

python - 从特定键开始迭代有序的字典项

sorting - 对 map slice 进行排序

python - 使用变量 NumPy 切片保留维度

go - 使用数组符号在 GoLang 中声明接口(interface)

python - 使用 `gopy` ,如何正确地将 []string 从 Python 传递给 Go?

linux - 在 golang 中写在热敏打印机设备上