¿Por qué no puedo duplicar un slice con `copy()` en golang?


Necesito hacer una copia de un slice en go y leyendo los documentos hay una función copy a mi disposición.

La función copy integrada copia elementos de un segmento de origen en un rebanada de destino. (Como caso especial, también copiará bytes de un cadena a una porción de bytes.) La fuente y el destino pueden superponerse. Copiar devuelve el número de elementos copiados, que será el mínimo de len (src) y len(dst).

Pero cuando do

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

Mi tmp está vacío como estaba antes (incluso traté de usar arr, tmp):

[]
[1 2 3]

Puedes comprobarlo en go playground. Entonces, ¿por qué no puedo copiar una rebanada?

 57
Author: Salvador Dali, 2015-05-12

5 answers

El builtin copy(dst, src) copia elementos min(len(dst), len(src)).

Así que si su dst está vacío (len(dst) == 0), nada será copiado.

Intente tmp := make([]int, len(arr)) (Ir al Parque Infantil):

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

Salida (como se esperaba):

[1 2 3]
[1 2 3]

Desafortunadamente esto no está documentado en el builtin paquete, pero está documentado en el Ir Especificación de idioma: Anexar y copiar sectores:

El número de elementos copiados es el mínimo de len(src) y len(dst).

Editar:

Finalmente se ha actualizado la documentación de copy() y ahora contiene el hecho de que se copiará la longitud mínima de origen y destino:

Copy devuelve el número de elementos copiados, que será el mínimo de len(src) y len(dst).

 112
Author: icza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-22 06:28:24

Si sus rebanadas fueran del mismo tamaño, funcionaría :

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

Daría:

3
[1 2 3]
[1 2 3]

Desde " Go Slices: uso e internos":

La función copy admite la copia entre segmentos de diferentes longitudes (se copiará solo hasta el menor número de elementos)

El ejemplo habitual es:

t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
 11
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-12 07:47:18

Otra forma sencilla de hacer esto es usando append que asignará el segmento en el proceso.

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

Salida (como se esperaba):

[1 2 3]
[1 2 3]

Así que una abreviatura para copiar matriz arr sería append([]int(nil), arr...)

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

 9
Author: Dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-26 22:21:06

Copy() se ejecuta durante la menor longitud de dst y src, por lo que debe inicializar el dst a la longitud deseada.

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

Salida:

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

Puede inicializar y copiar todos los elementos en una línea usando append() a un segmento nil.

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

Ejemplo:

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

Salida:

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

Comparando con allocation+copy(), para más de 1.000 elementos, use append. En realidad debajo de 1,000 la diferencia puede ser descuidada, que sea una regla de oro a menos que tenga muchos rebanada.

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
 8
Author: Esze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-02 22:29:32

La Especificación del Lenguaje de Programación Go

Añadir y copiar segmentos

La función copy copia los elementos slice de un src de origen a un destino dst y devuelve el número de elementos copiados. Ambos los argumentos deben tener el mismo tipo de elemento T y deben ser asignables a una rebanada de tipo []T. El número de elementos copiados es el mínimo de len (src) y len (dst). Como un caso especial, copia también acepta un destino argumento asignable al tipo [] byte con un argumento de origen de un tipo de cadena. Este formulario copia los bytes de la cadena en el byte slice.

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

tmp necesita espacio suficiente para arr. Por ejemplo,

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

Salida:

[1 2 3]
[1 2 3]
 2
Author: peterSO,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-12 05:37:53