英文:
Clone float slice in Go without affecting the original
问题
我想在Go中克隆一个[][]float64
切片,而不影响原始数组。我该如何做呢?
在下面的示例中,我希望在更改a2
时,切片a1
保持不变。目前,我正在使用Go的内置append函数。但是我没有能够获得所需的功能。
package main
import (
"fmt"
)
func main() {
a1 := [][]float64{[]float64{1.0,1.0}, []float64{2.0,2.1}}
a2 := make([][]float64, len(a1))
a2 = append([][]float64{}, a1...)
fmt.Println(a2, a1) // 在这一步,a1没有改变。
a2[1][0] = 5.0 // 改变a2的一个元素的值。
fmt.Println(a2, a1) // 在这一步,a1已经改变。
}
>> [[1 1] [2 2.1]] [[1 1] [2 2.1]]
>> [[1 1] [5 2.1]] [[1 1] [5 2.1]]
当我使用Go的copy函数时,我发现它只支持int
数据类型。当我使用copy函数时,我得到以下错误。可以理解的是,下面的错误是由于copy在Go中期望的类型不匹配。
cannot use copy(a2, a1) (type int) as type [][]float64 in assignment
我想使用切片而不是数组。
我正在使用这个参考资料。我是Go的新手,将非常感谢任何帮助。
英文:
I want to clone a [][]float64 slice
in Go
without affecting the original array. How can I do it?
In the below example, I want the slice a1
to remain unaffected when a2
is changed. At present, I am using the built in append function of Go. But I have not been able to get the desired functionality.
package main
import (
"fmt"
)
func main() {
a1 := [][]float64{[]float64{1.0,1.0}, []float64{2.0,2.1}}
a2 := make([][]float64, len(a1))
a2 = append([][]float64{}, a1...)
fmt.Println(a2, a1) // At this step, a1 has not changed.
a2[1][0] = 5.0 //Change value of one element of a2.
fmt.Println(a2, a1) // At this step, a1 has changed.
}
>> [[1 1] [2 2.1]] [[1 1] [2 2.1]]
>> [[1 1] [5 2.1]] [[1 1] [5 2.1]]
When I use copy function of Go, I find that it supports int
datatype. I get the following error when I use the copy function. Understandably, the below error is because of Type mismatch between what copy expects in Go.
cannot use copy(a2, a1) (type int) as type [][]float64 in assignment
I want to use slices and not arrays.
I am using this reference. I am new to Go and will appreciate any help.
答案1
得分: 3
多维切片是切片的切片。对于二维切片,你需要逐个克隆每个切片,可以使用循环来实现。
代码示例:
package main
import (
"fmt"
)
func Clone(arr [][]float64) (res [][]float64) {
res = make([][]float64, len(arr))
for i := range arr {
res[i] = append([]float64{}, arr[i]...)
}
return
}
func main() {
a1 := [][]float64{{1.0, 1.0}, {2.0, 2.1}}
a2 := Clone(a1)
fmt.Println(a2, a1) // 在这一步,a1 没有改变。
a2[1][0] = 5.0 // 改变 a2 中的一个元素的值。
fmt.Println(a2, a1)
}
输出结果:
[[1 1] [2 2.1]] [[1 1] [2 2.1]]
[[1 1] [5 2.1]] [[1 1] [2 2.1]]
英文:
A multi-dimensional slice is a slice of slices. You have to clone each slice individually, in case of a 2D slice using a loop.
Something like this:
package main
import (
"fmt"
)
func Clone(arr [][]float64) (res [][]float64) {
res = make([][]float64, len(arr))
for i := range arr {
res[i] = append([]float64{}, arr[i]...)
}
return
}
func main() {
a1 := [][]float64{{1.0, 1.0}, {2.0, 2.1}}
a2 := Clone(a1)
fmt.Println(a2, a1) // At this step, a1 has not changed.
a2[1][0] = 5.0 //Change value of one element of a2.
fmt.Println(a2, a1)
}
Prints
[[1 1] [2 2.1]] [[1 1] [2 2.1]]
[[1 1] [5 2.1]] [[1 1] [2 2.1]]
答案2
得分: 3
你可以使用range
方法来迭代你的二维切片,在循环内部创建一个与内部切片长度相同的临时切片,并将其赋值给目标切片索引,然后可以使用copy
方法。以下是具有相同逻辑的代码:
package main
import (
"fmt"
)
func main() {
a1 := [][]float64{[]float64{1.0, 1.0}, []float64{2.0, 2.1}}
a3 := make([][]float64, len(a1))
copySlice(a3, a1)
fmt.Println(a3, a1)
a3[1][0] = 10.0
fmt.Println(a3, a1)
}
func copySlice(dest [][]float64, src [][]float64) {
for i := range src {
tmp := make([]float64, len(src[i]))
dest[i] = tmp
copy(dest[i], src[i])
}
return
}
输出结果:
[[1 1] [2 2.1]] [[1 1] [2 2.1]]
[[1 1] [10 2.1]] [[1 1] [2 2.1]]
这段代码使用copySlice
函数将切片a1
的值复制到切片a3
中,并进行了一些修改和打印输出。
英文:
You can use the range
method to iterate over your 2D slice,inside the loop create a temporary slice of the same length as your inner slice and assign it to destination slice index,after that you can use the copy
method.Here is the code with same logic:
package main
import (
"fmt"
)
func main() {
a1 := [][]float64{[]float64{1.0, 1.0}, []float64{2.0, 2.1}}
a3 := make([][]float64, len(a1))
copySlice(a3, a1)
fmt.Println(a3, a1)
a3[1][0] = 10.0
fmt.Println(a3, a1)
}
func copySlice(dest [][]float64, src [][]float64) {
for i := range src {
tmp := make([]float64, len(src[i]))
dest[i] = tmp
copy(dest[i], src[i])
}
return
}
Output:
[[1 1] [2 2.1]] [[1 1] [2 2.1]]
[[1 1] [10 2.1]] [[1 1] [2 2.1]]
答案3
得分: 2
提供的答案是正确的,但是它们在循环中分配内存,而内存分配是昂贵的。
存在一种更优化的方法。通常情况下,你可以使用两次make
调用来创建和填充一个二维切片:一次小的调用用于保存切片头部,一次大的调用用于保存所有的值。关键在于,每个结果切片都是大缓冲区的一个切片。
func AllocAndCopy(arr [][]float64) (res [][]float64) {
// 为结果分配切片的切片
res = make([][]float64, len(arr))
// 为内容分配一个大的切片
size := 0
for _, x := range arr {
size += len(x)
}
mem := make([]float64, size)
// 在复制过程中不再分配内存
for i, x := range arr {
res[i] = mem[:len(x):len(x)]
mem = mem[len(x):]
copy(res[i], x)
}
return
}
在这个基准测试中,对于非常小的样本值,AllocAndCopy
的速度是Clone
和copySlice
的约2倍。
如果所有的切片具有相同的长度而不是任意长度,那么可以用更少的代码编写AllocAndCopy
。
英文:
The provided answers are correct, however they allocate memory in a loop, and memory allocation is expensive.
There exists a more optimized way. In general you can create and fill a 2D slice with exactly 2 calls to make
: 1 small to hold slice headers, and 1 large to hold all the values contiguously. The trick is that each result slice is a ... slice of the large buffer.
func AllocAndCopy(arr [][]float64) (res [][]float64) {
// 1 alloc of slice of slices, for the result
res = make([][]float64, len(arr))
// 1 alloc of a large slice, for the contents
size := 0
for _, x := range arr {
size += len(x)
}
mem := make([]float64, size)
// No more alloc during the copying
for i, x := range arr {
res[i] = mem[:len(x):len(x)]
mem = mem[len(x):]
copy(res[i], x)
}
return
}
In this benchmark with very small sample values, AllocAndCopy
is ~2x as fast as Clone
and copySlice
.
It is possible to write AllocAndCopy
with fewer lines of code if all the slices have the exact same length, instead of an arbitrary length for each.
答案4
得分: 1
复制切片的元素是可行的,但复制一个切片是不行的,因为切片本身并不包含元素。它只是对底层数组的引用,当操作切片时,并不会复制底层数组。即使在切片上使用copy
命令,也不会得到底层数组的副本。
这在《Go语言之旅》中有描述:https://tour.golang.org/moretypes/8
> 切片类似于对数组的引用
>
> 切片本身不存储任何数据,它只是描述底层数组的一部分。
>
> 修改切片的元素会修改其底层数组对应的元素。
>
> 其他共享同一底层数组的切片也会看到这些变化。
英文:
Copying the elements of slice works, but copying a slice does not because a slice does not contain the elements itself. It acts as references to an underlying array which is not duplicated when manipulating slices. Even using the copy
command on a slice won't result in a duplicate of the underlying array.
This is described in A Tour of Go: https://tour.golang.org/moretypes/8
> Slices are like references to arrays
>
> A slice does not store any data, it just describes a section of an underlying array.
>
> Changing the elements of a slice modifies the corresponding elements of its underlying array.
>
> Other slices that share the same underlying array will see those changes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论