Concaténer deux tranches dans Go


478

J'essaie de combiner la tranche [1, 2]et la tranche [3, 4]. Comment puis-je faire cela dans Go?

J'ai essayé:

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

mais j'ai:

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

Cependant, la documentation semble indiquer que c'est possible, que me manque-t-il?

slice = append(slice, anotherSlice...)

Réponses:


879

Ajoutez des points après la deuxième tranche:

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

C'est comme n'importe quelle autre fonction variadique.

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()une fonction variadique et le ...vous permet de passer plusieurs arguments à une fonction variadique à partir d'une tranche.

11
Est-ce que c'est du tout performant quand les tranches sont assez grosses? Ou le compilateur ne passe-t-il pas vraiment tous les éléments en paramètres?
Crapaud du

15
@Toad: Il ne les diffuse pas réellement. Dans l' foo()exemple ci-dessus, le isparamètre contient une copie de la tranche d'origine, c'est-à-dire qu'il a une copie de la référence légère au même tableau, len et cap sous-jacent. Si la foofonction modifie un membre, la modification sera visible sur l'original. Voici une démo . Ainsi, le seul vrai surcoût sera qu'il crée une nouvelle tranche si vous n'en avez pas déjà une, comme: foo(1, 2, 3, 4, 5)qui créera une nouvelle tranche qui istiendra.

2
Ah. Si je comprends bien, la fonction variadique est en fait implémentée comme un tableau de paramètres (au lieu de chaque paramètre de la pile)? Et puisque vous passez dans la tranche, il mappe en fait un à un?
Crapaud du

@Toad: Oui, lorsque vous utilisez ...une tranche existante, elle passe simplement cette tranche. Lorsque vous passez des arguments individuels, il les rassemble dans une nouvelle tranche et la transmet. Je n'ai pas connaissance de première main de la mécanique exacte, mais je suppose que cela: foo(1, 2, 3, 4, 5)et ceci: func foo(is ...int) {Just de sucres à ceci: foo([]int{1, 2, 3, 4, 5})et ceci: func foo(is []int) {.

77

Ajout et copie de tranches

La fonction variadique appendajoute zéro ou plusieurs valeurs xà s de type S, qui doit être un type de tranche, et renvoie la tranche résultante, également de type S. Les valeurs xsont passées à un paramètre de type ...TTest le type d'élément de Set les règles de passage de paramètres respectives s'appliquent. Dans un cas particulier, append accepte également un premier argument assignable à type []byteavec un deuxième argument de stringtype suivi de .... Ce formulaire ajoute les octets de la chaîne.

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}

Passer des arguments à ... paramètres

Si fest variadique avec le type de paramètre final ...T, alors dans la fonction, l'argument est équivalent à un paramètre de type []T. À chaque appel de f, l'argument passé au paramètre final est une nouvelle tranche de type []Tdont les éléments successifs sont les arguments réels, qui doivent tous être assignables au type T. La longueur de la tranche est donc le nombre d'arguments liés au paramètre final et peut différer pour chaque site d'appel.

La réponse à votre question est un exemple s3 := append(s2, s0...)dans la spécification du langage de programmation Go . Par exemple,

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

6
Remarque: l'utilisation générale de append (slice1, slice2 ...) me semble assez dangereuse. Si slice1 est une tranche d'un tableau plus grand, les valeurs de ce tableau seront écrasées par slice2. (Cela me fait grincer des dents que cela ne semble pas être une préoccupation commune?)
Hugo

7
@Hugo Si vous "remettez" une tranche de votre tableau, sachez que le "propriétaire" de la tranche pourra voir / écraser les parties du tableau qui dépassent la longueur actuelle de la tranche. Si vous ne le souhaitez pas, vous pouvez utiliser une expression de tranche complète (sous la forme de a[low : high : max]) qui spécifie également la capacité maximale . Par exemple, la tranche a[0:2:4]aura une capacité de 4et il ne peut pas être redimensionné pour inclure des éléments au-delà de cela, même si le tableau de support a un millier d'éléments après cela.
icza

30

Rien contre les autres réponses, mais j'ai trouvé la brève explication dans les documents plus facilement compréhensible que les exemples en eux:

func append

func append(slice []Type, elems ...Type) []TypeLa fonction intégrée append ajoute des éléments à la fin d'une tranche. S'il a une capacité suffisante, la destination est redimensionnée pour accueillir les nouveaux éléments. Si ce n'est pas le cas, un nouveau tableau sous-jacent sera alloué. Append renvoie la tranche mise à jour. Il est donc nécessaire de stocker le résultat de l'ajout, souvent dans la variable contenant la tranche elle-même:

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

Dans un cas spécial, il est légal d'ajouter une chaîne à une tranche d'octets, comme ceci:

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

1
Je vous remercie! Précieux pour moi!
Korjavin Ivan

23

Je pense qu'il est important de souligner et de savoir que si la tranche de destination (la tranche à laquelle vous ajoutez) a une capacité suffisante, l'ajout se fera "sur place", en redimensionnant la destination (redimensionner pour augmenter sa longueur afin d'être pouvant accueillir les éléments annexes).

Cela signifie que si la destination a été créée en découpant un tableau ou une tranche plus grand qui a des éléments supplémentaires au-delà de la longueur de la tranche résultante, ils peuvent être écrasés.

Pour démontrer, voir cet exemple:

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)

Sortie (essayez-la sur le 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]

Nous avons créé un tableau "de support" ade longueur 10. Ensuite, nous créons la xtranche de destination en découpant ce atableau, la ytranche est créée à l'aide du littéral composite []int{3, 4}. Maintenant , quand nous ajoutons yà x, le résultat est le prévu [1 2 3 4], mais ce qui peut être surprenant est que le réseau de soutien a aégalement changé, parce que la capacité de xest -ce 10qui suffit à ajouter yà elle, donc xest resliced qui utilisera également le même atableau de sauvegarde, et append()copiera des éléments de ydedans là-dedans.

Si vous voulez éviter cela, vous pouvez utiliser une expression de tranche complète qui a la forme

a[low : high : max]

qui construit une tranche et contrôle également la capacité de la tranche résultante en la définissant sur max - low.

Voir l'exemple modifié (la seule différence est que nous créons xcomme ceci 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)

Sortie (essayez-le sur le 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]

Comme vous pouvez le voir, nous obtenons le même xrésultat mais le tableau de sauvegarde an'a pas changé, car la capacité de xétait "seulement" 2(grâce à l'expression de tranche complète a[:2:2]). Donc, pour faire l'ajout, un nouveau tableau de support est alloué qui peut stocker les éléments des deux xet y, qui est distinct de a.


2
C'est très utile pour le problème auquel je suis confronté. Merci.
Aidy

9

Je voudrais souligner la réponse @icza et la simplifier un peu car c'est un concept crucial. Je suppose que le lecteur connaît bien les tranches .

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

Ceci est une réponse valable à la question. MAIS si vous devez utiliser les tranches «a» et «c» plus tard dans le code dans un contexte différent, ce n'est pas le moyen sûr de concaténer les tranches.

Pour expliquer, lisons l'expression non pas en termes de tranches, mais en termes de tableaux sous-jacents:

"Prenez le tableau (sous-jacent) de 'a' et ajoutez-y les éléments du tableau 'b'. Si le tableau 'a' a suffisamment de capacité pour inclure tous les éléments de 'b' - le tableau sous-jacent de 'c' ne sera pas un nouveau tableau , ce sera en fait le tableau «a». Fondamentalement, la tranche «a» affichera les éléments len (a) du tableau sous-jacent «a» et la tranche «c» affichera len (c) du tableau «a». "

append () ne crée pas nécessairement un nouveau tableau! Cela peut conduire à des résultats inattendus. Voir l' exemple Go Playground .

Utilisez toujours la fonction make () si vous voulez vous assurer que le nouveau tableau est alloué pour la tranche. Par exemple, voici quelques options laides mais assez efficaces pour la tâche.

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)

Merci d'avoir signalé ces effets secondaires. Contrastant étonnamment avec ce szenario modifié. play.golang.org/p/9FKo5idLBj4 Bien qu'en fournissant une capacité excédentaire, il faut bien réfléchir à ces effets secondaires déroutants contre une intuition plausible.
olippuner

5

Fonction append () et opérateur d'étalement

Deux tranches peuvent être concaténées en utilisant la appendméthode de la bibliothèque standard de golang. Ce qui est similaire au variadicfonctionnement de la fonction. Nous devons donc utiliser...

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 sortie du code ci-dessus est: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)marchera. Passer des arguments aux ...paramètres.

Si fest variadique avec un paramètre final pde type ...T, alors dans fle type de pest équivalent à type []T.

Si fest invoqué sans argument réel pour p, la valeur passée à pest nil.

Sinon, la valeur transmise est une nouvelle tranche de type []Tavec un nouveau tableau sous-jacent dont les éléments successifs sont les arguments réels, auxquels tous doivent être attribuables T. La longueur et la capacité de la tranche sont donc le nombre d'arguments liés à pet peuvent différer pour chaque site d'appel.

Compte tenu de la fonction et des appels

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.