web-dev-qa-db-ja.com

Golangで `copy()`でスライスを複製できないのはなぜですか?

Goでスライスのコピーを作成する必要があり、ドキュメントを読むと、自由に copy 関数があります。

Copy組み込み関数は、ソーススライスからターゲットスライスに要素をコピーします。 (特殊なケースとして、文字列からバイトをスライスにコピーします。)ソースと宛先が重複する場合があります。コピーは、コピーされた要素の数を返します。これは、len(src)とlen(dst)の最小値になります。

しかし、私がするとき:

arr := []int{1, 2, 3}
tmp := []int{}
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

私のtmpは以前のように空です(arr, tmpを使用しようとしました):

[]
[1 2 3]

外出先で確認できます playground 。では、なぜスライスをコピーできないのですか?

88
Salvador Dali

組み込みの copy(dst, src) は、min(len(dst), len(src))要素をコピーします。

したがって、dstが空(len(dst) == 0)の場合、何もコピーされません。

tmp := make([]int, len(arr))を試してください( Go Playground ):

arr := []int{1, 2, 3}
tmp := make([]int, len(arr))
copy(tmp, arr)
fmt.Println(tmp)
fmt.Println(arr)

出力(予想どおり):

[1 2 3]
[1 2 3]

残念ながら、これは builtin パッケージには文書化されていませんが、 Go言語仕様:スライスへの追加とコピー

コピーされる要素の数は、len(src)len(dst)の最小値です。

編集:

最後に、copy()のドキュメントが更新され、ソースと宛先の最小長がコピーされるという事実が含まれるようになりました。

コピーは、コピーされた要素の数を返します。これは、len(src)とlen(dst)のminimumになります。

152
icza

別の簡単な方法は、プロセスでスライスを割り当てるappendを使用することです。

arr := []int{1, 2, 3}
tmp := append([]int(nil), arr...)  // Notice the ... splat
fmt.Println(tmp)
fmt.Println(arr)

出力(予想どおり):

[1 2 3]
[1 2 3]

したがって、配列arrをコピーするための省略形はappend([]int(nil), arr...)になります。

https://play.golang.org/p/sr_4ofs5GW

15
Dave

スライスが同じサイズの場合、- それは動作します

arr := []int{1, 2, 3}
tmp := []int{0, 0, 0}
i := copy(tmp, arr)
fmt.Println(i)
fmt.Println(tmp)
fmt.Println(arr)

与えるだろう:

3
[1 2 3]
[1 2 3]

Go Slices:usage and internals 」から:

コピー機能は、異なる長さのスライス間でのコピーをサポートします(少数の要素までしかコピーしません

通常の例は次のとおりです。

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
12
VonC

Copy()はdstとsrcの最小の長さで実行されるため、dstを目的の長さに初期化する必要があります。

A := []int{1, 2, 3}
B := make([]int, 3)
copy(B, A)
C := make([]int, 2)
copy(C, A)
fmt.Println(A, B, C)

出力:

[1 2 3] [1 2 3] [1 2]

Append()を使用してnilスライスにすべての要素を初期化し、1行でコピーできます。

x := append([]T{}, []...)

例:

A := []int{1, 2, 3}
B := append([]int{}, A...)
C := append([]int{}, A[:2]...)
fmt.Println(A, B, C)    

出力:

[1 2 3] [1 2 3] [1 2]

1,000個を超える要素について、allocation + copy()と比較するには、appendを使用します。実際に1,000を下回ると、その差は無視される可能性があります。スライスが多くない場合は、経験則としてお試しください。

BenchmarkCopy1-4                50000000            27.0 ns/op
BenchmarkCopy10-4               30000000            53.3 ns/op
BenchmarkCopy100-4              10000000           229 ns/op
BenchmarkCopy1000-4              1000000          1942 ns/op
BenchmarkCopy10000-4              100000         18009 ns/op
BenchmarkCopy100000-4              10000        220113 ns/op
BenchmarkCopy1000000-4              1000       2028157 ns/op
BenchmarkCopy10000000-4              100      15323924 ns/op
BenchmarkCopy100000000-4               1    1200488116 ns/op
BenchmarkAppend1-4              50000000            34.2 ns/op
BenchmarkAppend10-4             20000000            60.0 ns/op
BenchmarkAppend100-4             5000000           240 ns/op
BenchmarkAppend1000-4            1000000          1832 ns/op
BenchmarkAppend10000-4            100000         13378 ns/op
BenchmarkAppend100000-4            10000        142397 ns/op
BenchmarkAppend1000000-4            2000       1053891 ns/op
BenchmarkAppend10000000-4            200       9500541 ns/op
BenchmarkAppend100000000-4            20     176361861 ns/op
9
Esze

Goプログラミング言語仕様

スライスへの追加とコピー

関数copyは、ソースsrcから宛先dstにスライス要素をコピーし、コピーされた要素の数を返します。両方の引数は同一の要素タイプTを持ち、タイプ[] Tのスライスに割り当て可能でなければなりません。コピーされる要素の数は、len(src)とlen(dst)の最小値です。特殊なケースとして、copyは、文字列型のソース引数とともに[] byte型に割り当て可能な宛先引数も受け入れます。この形式は、文字列からバイトスライスにバイトをコピーします。

copy(dst, src []T) int
copy(dst []byte, src string) int

tmpには、arrに十分なスペースが必要です。例えば、

package main

import "fmt"

func main() {
    arr := []int{1, 2, 3}
    tmp := make([]int, len(arr))
    copy(tmp, arr)
    fmt.Println(tmp)
    fmt.Println(arr)
}

出力:

[1 2 3]
[1 2 3]
2
peterSO