Verketten Sie zwei Scheiben in Go


477

Ich versuche, die Scheibe [1, 2]und die Scheibe zu kombinieren[3, 4] . Wie kann ich das in Go machen?

Ich habe es versucht:

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

aber bekam:

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

Die Dokumentation scheint jedoch darauf hinzudeuten, dass dies möglich ist. Was fehlt mir?

slice = append(slice, anotherSlice...)

Antworten:


878

Fügen Sie nach der zweiten Scheibe Punkte hinzu:

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

Dies ist genau wie bei jeder anderen variadischen Funktion.

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()eine Variadic-Funktion, mit der ...Sie mehrere Argumente aus einem Slice an eine Variadic-Funktion übergeben können.

11
Ist das überhaupt performant, wenn die Scheiben ziemlich groß sind? Oder übergibt der Compiler nicht wirklich alle Elemente als Parameter?
Kröte

15
@Toad: Es verbreitet sie nicht wirklich. Im foo()obigen Beispiel enthält der isParameter eine Kopie des ursprünglichen Slice, dh er enthält eine Kopie der leichten Referenz auf dasselbe zugrunde liegende Array, len und cap. Wenn die fooFunktion ein Mitglied ändert, wird die Änderung auf dem Original angezeigt. Hier ist eine Demo . Der einzige wirkliche Aufwand besteht darin, dass ein neues Slice erstellt wird, wenn Sie noch keines hatten, z. B.: foo(1, 2, 3, 4, 5)Dadurch wird ein neues Slice erstellt, isdas haltbar ist.

2
Ah. Wenn ich das richtig verstehe, wird die Variadic-Funktion tatsächlich wie ein Array von Parametern implementiert (anstelle jedes Parameters auf dem Stapel)? Und da Sie das Slice übergeben, wird es tatsächlich eins zu eins abgebildet?
Kröte

@Toad: Ja, wenn Sie ...ein vorhandenes Slice verwenden, wird dieses Slice einfach übergeben. Wenn Sie einzelne Argumente übergeben, werden diese in einem neuen Slice zusammengefasst und übergeben. Ich habe keine Kenntnisse aus erster Hand über die genaue Mechanik, aber ich würde vermuten, dass dies: foo(1, 2, 3, 4, 5)und dies: func foo(is ...int) {nur Zucker zu diesem: foo([]int{1, 2, 3, 4, 5})und dies : func foo(is []int) {.

77

Anhängen an und Kopieren von Slices

Die Variadic-Funktion appendhängt null oder mehr Werte xan den s Typ an S, der ein Slice-Typ sein muss, und gibt das resultierende Slice zurück, ebenfalls vom Typ S. Die Werte xwerden an einen Parameter vom Typ übergeben, ...Twobei Tder Elementtyp von Sund die jeweiligen Regeln für die Parameterübergabe gelten. Als Sonderfall akzeptiert append auch ein erstes Argument, das dem Typ zugewiesen werden kann, gefolgt von []byteeinem zweiten Argument vom stringTyp .... Dieses Formular hängt die Bytes der Zeichenfolge an.

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}

Argumente an ... Parameter übergeben

Wenn fes mit dem endgültigen Parametertyp variadisch ...Tist, entspricht das Argument innerhalb der Funktion einem Parameter vom Typ []T. Bei jedem Aufruf von fist das an den letzten Parameter übergebene Argument ein neues Slice vom Typ, []Tdessen aufeinanderfolgende Elemente die tatsächlichen Argumente sind, die alle dem Typ zuweisbar sein müssen T. Die Länge des Slice ist daher die Anzahl der Argumente, die an den letzten Parameter gebunden sind, und kann für jede Aufrufstelle unterschiedlich sein.

Die Antwort auf Ihre Frage finden Sie s3 := append(s2, s0...)in der Go-Programmiersprachenspezifikation . Zum Beispiel,

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

6
Hinweis: Die allgemeine Verwendung von append (Slice1, Slice2 ...) scheint mir ziemlich gefährlich zu sein. Wenn Slice1 ein Slice eines größeren Arrays ist, werden die Werte dieses Arrays von Slice2 überschrieben. (Es lässt mich zusammenzucken, dass dies kein allgemeines Problem zu sein scheint?)
Hugo

7
@Hugo Wenn Sie ein Slice Ihres Arrays "übergeben", wissen Sie, dass der Slice- "Eigentümer" Teile des Arrays sehen / überschreiben kann, die über die aktuelle Länge des Slice hinausgehen. Wenn Sie dies nicht möchten, können Sie einen vollständigen Slice-Ausdruck (in Form von a[low : high : max]) verwenden, der auch die maximale Kapazität angibt . Zum Beispiel hat das Slice a[0:2:4]eine Kapazität von 4und es kann nicht neu geschnitten werden, um darüber hinausgehende Elemente einzuschließen, selbst wenn das Hintergrundarray danach tausend Elemente enthält.
icza

30

Nichts gegen die anderen Antworten, aber ich fand die kurze Erklärung in den Dokumenten leichter verständlich als die Beispiele in ihnen:

func anhängen

func append(slice []Type, elems ...Type) []TypeDie integrierte Funktion zum Anhängen hängt Elemente an das Ende eines Slice an. Wenn die Kapazität ausreichend ist, wird das Ziel neu aufgeteilt, um die neuen Elemente aufzunehmen. Ist dies nicht der Fall, wird ein neues zugrunde liegendes Array zugewiesen. Anhängen gibt das aktualisierte Slice zurück. Es ist daher notwendig, das Ergebnis des Anhängens zu speichern, häufig in der Variablen, die das Slice selbst enthält:

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

Als Sonderfall ist es zulässig, eine Zeichenfolge wie folgt an ein Byte-Slice anzuhängen:

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

1
Vielen Dank! Wertvoll für mich!
Korjavin Ivan

23

Ich denke, es ist wichtig darauf hinzuweisen und zu wissen, dass, wenn das Ziel-Slice (das Slice, an das Sie anhängen) über eine ausreichende Kapazität verfügt, das Anhängen "an Ort und Stelle" erfolgt, indem das Ziel neu aufgeteilt wird (erneutes Schneiden, um seine Länge zu erhöhen, um zu sein) in der Lage, die anhängbaren Elemente aufzunehmen).

Dies bedeutet, dass wenn das Ziel durch Schneiden eines größeren Arrays oder Slice erstellt wurde, das zusätzliche Elemente enthält, die über die Länge des resultierenden Slice hinausgehen, diese möglicherweise überschrieben werden.

Zur Demonstration sehen Sie dieses Beispiel:

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)

Ausgabe (versuchen Sie es auf dem Go-Spielplatz ):

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]

Wir haben ein "Backing" -Array amit Länge erstellt 10. Dann erstellen wir das xZiel-Slice, indem wir dieses aArray aufteilen. Das ySlice wird mit dem zusammengesetzten Literal erstellt []int{3, 4}. Nun , wenn wir anhängen yzu x, ist das Ergebnis der erwartete [1 2 3 4], aber was werden kann , überraschend ist , dass die Träger Array aauch geändert, weil Kapazität xheißt , 10die ausreicht, anhängen , yum es, so xwird Resliced , die auch die gleiche verwenden aTrägeranordnung und append()kopiert dort Elemente von y.

Wenn Sie dies vermeiden möchten, können Sie einen vollständigen Slice-Ausdruck verwenden, der die Form hat

a[low : high : max]

Dadurch wird ein Slice erstellt und die Kapazität des resultierenden Slice durch Festlegen auf gesteuert max - low.

Siehe das modifizierte Beispiel (der einzige Unterschied ist , dass wir schaffen xwie folgt aus : 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)

Ausgabe (probieren Sie es auf dem 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]

Wie Sie sehen können, erhalten wir das gleiche xErgebnis, aber das Hintergrundarray ahat sich nicht geändert, da die Kapazität von x"nur" war 2(dank des vollständigen Slice-Ausdrucks a[:2:2]). Zum Anhängen wird ein neues Hintergrundarray zugewiesen, in dem die Elemente von beiden xund gespeichert werden können y, das sich von unterscheidet a.


2
Es ist sehr hilfreich für das Problem, mit dem ich konfrontiert bin. Vielen Dank.
Aidy

9

Ich möchte die Antwort von @icza hervorheben und ein wenig vereinfachen, da es sich um ein entscheidendes Konzept handelt. Ich gehe davon aus, dass der Leser mit Slices vertraut ist .

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

Dies ist eine gültige Antwort auf die Frage. ABER wenn Sie die Slices 'a' und 'c' später im Code in einem anderen Kontext verwenden müssen, ist dies nicht die sichere Methode, um Slices zu verketten.

Lesen wir zur Erklärung den Ausdruck nicht in Form von Slices, sondern in Form von zugrunde liegenden Arrays:

"Nehmen Sie das (zugrunde liegende) Array von 'a' und hängen Sie Elemente aus dem Array 'b' an. Wenn das Array 'a' über genügend Kapazität verfügt, um alle Elemente aus 'b' aufzunehmen, ist das zugrunde liegende Array von 'c' kein neues Array , es wird tatsächlich Array 'a' sein. Grundsätzlich zeigt Slice 'a' len (a) Elemente des zugrunde liegenden Arrays 'a' und Slice 'c' zeigt len ​​(c) des Arrays 'a'. "

append () erstellt nicht unbedingt ein neues Array! Dies kann zu unerwarteten Ergebnissen führen. Siehe Go Playground-Beispiel .

Verwenden Sie immer die Funktion make (), wenn Sie sicherstellen möchten, dass dem Slice ein neues Array zugewiesen wird. Zum Beispiel gibt es hier nur wenige hässliche, aber effizient genug Optionen für die Aufgabe.

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)

Vielen Dank für den Hinweis auf diese Nebenwirkungen. Erstaunlich im Gegensatz zu diesem modifizierten Szenario. play.golang.org/p/9FKo5idLBj4 Obwohl bei der Bereitstellung von Überkapazitäten sorgfältig über diese rätselhaften Nebenwirkungen gegen plausible Intuition nachgedacht werden sollte.
Olippuner

5

Funktion append () und Spread-Operator

Zwei Slices können mithilfe der appendMethode in der Standard-Golang-Bibliothek verkettet werden . Welches ist ähnlich wie die variadicFunktionsoperation. Also müssen wir verwenden...

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

Die Ausgabe des obigen Codes lautet: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)wird funktionieren. Argumente an übergeben... Parameter übergeben.

Wenn fist variadische mit einem letzten Parameter pvom Typ ...T, dann innerhalb von fder Art der päquivalent zu geben []T.

Wenn fohne tatsächliche Argumente für aufgerufen wird p, plautet der übergebene Wert is nil.

Andernfalls handelt es sich bei dem übergebenen Wert um ein neues Slice vom Typ []Tmit einem neuen zugrunde liegenden Array, dessen aufeinanderfolgende Elemente die tatsächlichen Argumente sind, denen alle zugewiesen werden müssen T. Die Länge und Kapazität des Slice ist daher die Anzahl der Argumente, die an pjede Aufrufstelle gebunden sind und sich für diese unterscheiden können.

Angesichts der Funktion und Aufrufe

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.