在Go语言中克隆浮点数切片而不影响原始切片的方法是什么?

huangapple go评论83阅读模式
英文:

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的速度是ClonecopySlice的约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.

huangapple
  • 本文由 发表于 2021年7月27日 17:51:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/68542702.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定