Concatenar dos rebanadas en Go


478

Estoy tratando de combinar el corte [1, 2]y el corte [3, 4]. ¿Cómo puedo hacer esto en Go?

Lo intenté:

append([]int{1,2}, []int{3,4})

pero tengo:

cannot use []int literal (type []int) as type int in append

Sin embargo, la documentación parece indicar que esto es posible, ¿qué me estoy perdiendo?

slice = append(slice, anotherSlice...)

Respuestas:


879

Agregue puntos después de la segunda rebanada:

//---------------------------vvv
append([]int{1,2}, []int{3,4}...)

Esto es como cualquier otra función variadic.

func foo(is ...int) {
    for i := 0; i < len(is); i++ {
        fmt.Println(is[i])
    }
}

func main() {
    foo([]int{9,8,7,6,5}...)
}

37
append()una función variable, y le ...permite pasar múltiples argumentos a una función variable desde un segmento.

11
¿Es esto algo efectivo cuando las rebanadas son bastante grandes? ¿O el compilador realmente no pasa todos los elementos como parámetros?
Sapo

15
@Toad: en realidad no los extiende. En el foo()ejemplo anterior, el isparámetro contiene una copia de la porción original, es decir, tiene una copia de la referencia ligera a la misma matriz subyacente, len y cap. Si la foofunción altera a un miembro, el cambio se verá en el original. Aquí hay una demostración . Entonces, la única sobrecarga real será que crea un nuevo segmento si aún no lo tenía, como: foo(1, 2, 3, 4, 5)lo que creará un nuevo segmento que isse mantendrá.

2
Ah Si entiendo correctamente, la función variadic se implementa realmente como una matriz de parámetros (en lugar de cada parámetro en la pila)? ¿Y como pasas el corte, en realidad se mapea uno a uno?
Sapo

@Toad: Sí, cuando se usa ...en un segmento existente, simplemente pasa ese segmento. Cuando pasa argumentos individuales, los reúne en un nuevo segmento y lo pasa. No tengo conocimiento de primera mano de la mecánica exacta, pero supongo que esto: foo(1, 2, 3, 4, 5)y esto: func foo(is ...int) {Just de azúcares a esto: foo([]int{1, 2, 3, 4, 5})y esto: func foo(is []int) {.

77

Agregar y copiar sectores

La función variadic appendagrega cero o más valores xa s type S, que debe ser un tipo de segmento, y devuelve el segmento resultante, también de tipo S. Los valores xse pasan a un parámetro de tipo ...Tdonde Tes el tipo de elemento Sy se aplican las respectivas reglas de paso de parámetros. Como caso especial, append también acepta un primer argumento asignable para escribir []bytecon un segundo argumento de stringtipo seguido de .... Este formulario agrega los bytes de la cadena.

append(s S, x ...T) S  // T is the element type of S

s0 := []int{0, 0}
s1 := append(s0, 2)        // append a single element     s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)  // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)    // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}

Pasar argumentos a ... parámetros

Si fes variable con el tipo de parámetro final ...T, entonces dentro de la función el argumento es equivalente a un parámetro de tipo []T. En cada llamada de f, el argumento pasado al parámetro final es una nueva porción de tipo []Tcuyos elementos sucesivos son los argumentos reales, todos los cuales deben ser asignables al tipo T. La longitud del segmento es, por lo tanto, el número de argumentos vinculados al parámetro final y puede diferir para cada sitio de llamada.

La respuesta a su pregunta es un ejemplo s3 := append(s2, s0...)en la especificación del lenguaje de programación Go . Por ejemplo,

s := append([]int{1, 2}, []int{3, 4}...)

66
Nota: el uso general de append (slice1, slice2 ...) me parece bastante peligroso. Si slice1 es un segmento de una matriz más grande, los valores de esa matriz serán sobrescritos por slice2. (¿Me da vergüenza que esto no parezca ser una preocupación común?)
Hugo

77
@Hugo Si "entrega" un segmento de su matriz, sepa que el "propietario" del segmento podrá ver / sobrescribir partes del conjunto que están más allá de la longitud actual del segmento. Si no desea esto, puede usar una expresión de corte completa (en forma de a[low : high : max]) que también especifica la capacidad máxima . Por ejemplo, el segmento a[0:2:4]tendrá una capacidad de 4y no puede ser cortado para incluir elementos más allá de eso, ni siquiera si la matriz de respaldo tiene mil elementos después de eso.
icza

30

Nada en contra de las otras respuestas, pero encontré la breve explicación en los documentos más fácilmente comprensible que los ejemplos en ellos:

func append

func append(slice []Type, elems ...Type) []TypeLa función incorporada append agrega elementos al final de un segmento. Si tiene capacidad suficiente, el destino se vuelve a acoplar para acomodar los nuevos elementos. Si no lo hace, se asignará una nueva matriz subyacente. Append devuelve el segmento actualizado. Por lo tanto, es necesario almacenar el resultado de append, a menudo en la variable que contiene el segmento:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

Como caso especial, es legal agregar una cadena a un segmento de bytes, de esta manera:

slice = append([]byte("hello "), "world"...)

1
¡Gracias! Valioso para mi!
Korjavin Ivan

23

Creo que es importante señalar y saber que si la porción de destino (la porción a la que anexas) tiene capacidad suficiente, la adición se realizará "en el lugar", mediante la división del destino (la reducción para aumentar su longitud para ser capaz de acomodar los elementos anexables).

Esto significa que si el destino se creó cortando una matriz o segmento más grande que tiene elementos adicionales más allá de la longitud del segmento resultante, pueden sobrescribirse.

Para demostrarlo, vea este ejemplo:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)

x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))

x = append(x, y...)
fmt.Printf("x: %v\n", x)

fmt.Printf("a: %v\n", a)

Salida (pruébalo en Go Playground ):

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]

Creamos una matriz de "respaldo" acon longitud 10. Luego creamos el xsegmento de destino dividiendo esta amatriz, el ysegmento se crea utilizando el literal compuesto []int{3, 4}. Ahora, cuando añadimos ya x, el resultado es el esperado [1 2 3 4], pero lo que puede ser sorprendente es que el conjunto de respaldo atambién ha cambiado, ya que la capacidad de xdecir 10que es suficiente para anexar ya la misma, por lo que xse resliced que también utilizar el mismo aconjunto de respaldo, y append()copiará elementos de yallí.

Si desea evitar esto, puede usar una expresión de corte completo que tenga la forma

a[low : high : max]

que construye un segmento y también controla la capacidad del segmento resultante configurándolo en max - low.

Vea el ejemplo modificado (la única diferencia es que creamos xasí x = a[:2:2]:

a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)

x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))

x = append(x, y...)
fmt.Printf("x: %v\n", x)

fmt.Printf("a: %v\n", a)

Salida (pruébalo en Go Playground )

a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]

Como puede ver, obtenemos el mismo xresultado, pero la matriz de respaldo ano cambió, porque la capacidad de xera "solo" 2(gracias a la expresión de corte completo a[:2:2]). Entonces, para hacer el apéndice, se asigna una nueva matriz de respaldo que puede almacenar los elementos de ambos xy y, que es diferente de a.


2
Es muy útil para el problema que estoy enfrentando. Gracias.
Aidy

9

Me gustaría enfatizar la respuesta de @icza y simplificarla un poco, ya que es un concepto crucial. Supongo que el lector está familiarizado con las rebanadas .

c := append(a, b...)

Esta es una respuesta válida a la pregunta. PERO si necesita usar los segmentos 'a' y 'c' más adelante en el código en un contexto diferente, esta no es la forma segura de concatenar los segmentos.

Para explicar, leamos la expresión no en términos de sectores, sino en términos de matrices subyacentes:

"Tome la matriz (subyacente) de 'a' y agregue elementos de la matriz 'b'. Si la matriz 'a' tiene suficiente capacidad para incluir todos los elementos de 'b', la matriz subyacente de 'c' no será una nueva matriz , en realidad será la matriz 'a'. Básicamente, la porción 'a' mostrará elementos len (a) de la matriz subyacente 'a', y la porción 'c' mostrará len (c) de la matriz 'a'. "

¡append () no necesariamente crea una nueva matriz! Esto puede conducir a resultados inesperados. Vea el ejemplo de Go Playground .

Utilice siempre la función make () si desea asegurarse de que se asigne una nueva matriz para el segmento. Por ejemplo, aquí hay algunas opciones lo suficientemente feas pero eficientes para la tarea.

la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)

la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)

Gracias por señalar estos efectos secundarios. Increíblemente contrastante con este szenario modificado. play.golang.org/p/9FKo5idLBj4 Aunque al proporcionar un exceso de capacidad, uno debe pensar cuidadosamente sobre estos efectos secundarios desconcertantes contra la intuición plausible.
olippuner

5

Función append () y operador de propagación

Se pueden concatenar dos appendsectores utilizando el método de la biblioteca estándar de golang. Que es similar a la variadicoperación de la función. Entonces necesitamos usar...

package main

import (
    "fmt"
)

func main() {
    x := []int{1, 2, 3}
    y := []int{4, 5, 6}
    z := append([]int{}, append(x, y...)...)
    fmt.Println(z)
}

La salida del código anterior es: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)trabajará. Pasar argumentos a... parámetros.

Si fes variable con un parámetro final pde tipo ...T, entonces dentro fdel tipo de pes equivalente a tipo[]T .

Si fse invoca sin argumentos reales para p, el valor pasado a pesnil .

De lo contrario, el valor pasado es una nueva porción de tipo []Tcon una nueva matriz subyacente cuyos elementos sucesivos son los argumentos reales, a los que todos deben poder asignarse T. La longitud y la capacidad del segmento es, por lo tanto, el número de argumentos vinculados ap y pueden diferir para cada sitio de llamada.

Dada la función y llamadas

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.