当前位置: 首页 > 知识库问答 >
问题:

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

赵智
2023-03-14

我正在学习围棋,我有一个理论问题。

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

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)
}

问题游乐场连接

我提出了一个可能的解决方案,但它毫无意义,因为它依赖于将切片3创建为空,并通过copy()将切片2复制到切片3上。没有捷径吗?

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)
}

解决方案游乐场链接

编辑:

我已经读到,在这个天真的例子中,有一种特殊的行为可以作为解决方案(见下文)。然而,在任何其他情况下,这都是行不通的。基本上,如果创建空切片时没有指定底层数组的大小,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)
}

操场上有被通缉的行为

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

编辑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)
}

游戏场

共有1个答案

经骁
2023-03-14

有几个人评论说我昨晚不够清楚。因此,我想澄清并提供一个我在@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发表了一篇评论,链接到一篇博客文章,解释了切片是如何工作的,如果你正在学习这门语言,这是一篇很好的文章。在一节中最重要的是一个可能的“明白”有一个对现实生活场景的“修复”的解释,可以推断为修复我的例子中的不一致性。(创建一个传递的切片的副本,并使用取而代之的是)
游乐场

 类似资料:
  • 我有三个表在我的应用程序,把它们称为,,和。有字段为和,两者都有索引。有字段带索引,有字段带索引。 当我执行以下查询时: 它真的很慢(约1秒)。 当我执行以下查询时: 速度非常快(约20毫秒)。 据我所知,这些桌子大小差不多 关于这两个查询之间的巨大性能差异,有什么想法吗? 表大小: > tableA:2061392行 表B:175339行 TableC: 1888912行 postgresql-

  • 问题内容: 我正在使用PDO 进行SQL查询的注册表单。插入之后,我想提取刚刚创建的(自动递增,主键),并将其放入另一个表(“确认代码”表)中。 但是,如何确定该用户名不是第二位用户的用户名,后者在第一位用户之后的1/1000秒内注册呢? 我应该找到某种方法锁定桌子吗?我应该使用交易吗? 问题答案: 返回 在该连接 上插入的最后一行的标识符,因此并发用户(与数据库具有不同的连接)不会受到干扰。

  • 问题内容: 我有一个简单的代码可以生成随机数 上面的方法被调用大约10次(不是循环的)。我想确保所有数字都是唯一的(假设)。 我可以确定每次呼叫都会得到唯一的号码吗?如果没有,我该如何解决? 编辑:我可能已经含糊其词了。我想避免手动检查,如果我真的有唯一的数字,所以我想知道是否有更好的解决方案。 问题答案: 有多种方法可以实现这一目标,哪种方法更合适,取决于您需要从多少个数字中选择。 如果要从大量

  • 我的问题是如何创建像这样的类? (内置类型)没有属性,即使这个的是。 而且它没有使用

  • 4.2. Slice Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。一个slice类型一般写作[]T,其中T代表slice中元素的类型;slice的语法和数组很像,只是没有固定长度而已。 数组和slice之间有着紧密的联系。一个slice是一个轻量级的数据结构,提供了访问数组子序列(或者全部)元素的功能,而且slice的底层确实引用一个数组对象。一个slice由三个部分构成:指针

  • slice 方法 从已有数组返回选定的元素。 语法: arrayObject.slice( start, end ); 参数说明: start - 必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。 可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 s