Połącz dwa plasterki w Go

477

Próbuję połączyć plasterek [1, 2]i plasterek [3, 4]. Jak mogę to zrobić w Go?

Próbowałem:

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

ale dostałem:

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

Jednak dokumentacja wskazuje na to, to jest możliwe, czego mi brakuje?

slice = append(slice, anotherSlice...)
Kevin Burke
źródło

Odpowiedzi:

877

Dodaj kropki po drugim plasterku:

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

To jest jak każda inna funkcja wariadyczna.

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

func main() {
    foo([]int{9,8,7,6,5}...)
}
użytkownik1106925
źródło
37
append()funkcja variadic, i ...pozwala przekazać wiele argumentów do funkcji variadic z wycinka.
11
Czy to w ogóle jest skuteczne, gdy plasterki są dość duże? Czy też kompilator nie przekazuje wszystkich elementów jako parametrów?
Toad
15
@Toad: W rzeczywistości ich nie rozprzestrzenia. W foo()powyższym przykładzie isparametr zawiera kopię oryginalnego wycinka, co oznacza, że ​​zawiera kopię lekkiego odwołania do tej samej podstawowej tablicy, soczewki i czapki. Jeśli foofunkcja zmieni element, zmiana będzie widoczna na oryginale. Oto demo . Więc jedynym prawdziwym narzutem będzie to, że tworzy nowy plasterek, jeśli jeszcze go nie masz, na przykład: foo(1, 2, 3, 4, 5)który utworzy nowy plasterek, który isbędzie trwał.
2
Ach Jeśli dobrze rozumiem, funkcja variadic jest faktycznie zaimplementowana jak tablica parametrów (zamiast każdego parametru na stosie)? A skoro podajesz plasterek, mapuje on jeden na jednego?
Toad
@ Załaduj: Tak, kiedy używasz ...istniejącego wycinka, po prostu przekazuje wycinek. Gdy przekazujesz poszczególne argumenty, gromadzi je w nowy kawałek i przekazuje. Nie mam wiedzę z pierwszej ręki o dokładnych mechaniki, ale ja myślę, że w ten sposób: foo(1, 2, 3, 4, 5)a to: func foo(is ...int) {tylko de-cukry do tego: foo([]int{1, 2, 3, 4, 5})a to: func foo(is []int) {.
77

Dołączanie i kopiowanie plasterków

Funkcja variadic appenddołącza zero lub więcej wartości xdo s typu S, który musi być typem plastra, i zwraca wynikowy plasterek, również typu S. Wartości xsą przekazywane do parametru typu, ...Tgdzie Tjest typ elementu Si obowiązują odpowiednie reguły przekazywania parametrów. W specjalnym przypadku append przyjmuje również pierwszy argument przypisany do pisania, []bytea następnie drugi argument stringtypu .... Ten formularz dołącza bajty ciągu.

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}

Przekazywanie argumentów do ... parametrów

Jeśli fjest variadic z ostatecznym typem parametru ...T, to w funkcji argument jest równoważny parametrowi typu []T. Przy każdym wywołaniu fargument przekazywany do parametru końcowego jest nowym wycinkiem typu, []Tktórego kolejnymi elementami są rzeczywiste argumenty, które wszystkie muszą być przypisane do danego typu T. Długość wycinka jest zatem liczbą argumentów związanych z końcowym parametrem i może różnić się dla każdej strony wywoływania.

Odpowiedź na twoje pytanie jest przykładem s3 := append(s2, s0...)w specyfikacji języka programowania Go . Na przykład,

s := append([]int{1, 2}, []int{3, 4}...)
peterSO
źródło
6
Uwaga: ogólne użycie append (slice1, slice2 ...) wydaje mi się dość niebezpieczne. Jeśli slice1 jest plasterkiem większej tablicy, wartości tej tablicy zostaną zastąpione przez slice2. (Czuję, że kulę się, że nie wydaje się to powszechną troską?)
Hugo
7
@ Hugo Jeśli „przekażesz” plasterek swojej tablicy, wiedz, że plasterek „właściciel” będzie mógł zobaczyć / zastąpić części tablicy, które są poza bieżącą długością plastra. Jeśli nie chcesz tego, możesz użyć wyrażenia pełnego wycinka (w postaci a[low : high : max]), które określa również maksymalną pojemność . Na przykład plasterek a[0:2:4]będzie miał pojemność 4i nie będzie można go ponownie przeskalować, aby uwzględnić elementy poza tym, nawet jeśli tablica podkładowa ma później tysiąc elementów.
icza
30

Nie ma nic przeciwko innym odpowiedziom, ale znalazłem krótkie wyjaśnienie w dokumentacji łatwiejsze do zrozumienia niż zawarte w nich przykłady:

appc append

func append(slice []Type, elems ...Type) []TypeDołącz wbudowaną funkcję dołącza elementy na końcu wycinka. Jeśli ma wystarczającą pojemność, miejsce docelowe jest przeskalowywane, aby pomieścić nowe elementy. Jeśli nie, zostanie przydzielona nowa podstawowa tablica. Append zwraca zaktualizowany plasterek. Dlatego konieczne jest zapisanie wyniku append, często w zmiennej zawierającej sam plasterek:

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

W specjalnym przypadku dozwolone jest dołączanie ciągu do wycinka bajtu, jak poniżej:

slice = append([]byte("hello "), "world"...)
Fiatjaf
źródło
1
Dziękuję Ci! Cenne dla mnie!
Korjavin Ivan
23

Uważam, że ważne jest, aby zwrócić uwagę i wiedzieć, że jeśli wycinek docelowy (wycinek, do którego się dołączasz) ma wystarczającą pojemność, dołączenie nastąpi „w miejscu” poprzez przeskalowanie miejsca docelowego (przeskalowanie w celu zwiększenia jego długości, aby w stanie pomieścić elementy dodatkowe).

Oznacza to, że jeśli miejsce docelowe zostało utworzone przez wycięcie większej tablicy lub plasterka, który ma dodatkowe elementy poza długość wynikowego plasterka, mogą zostać zastąpione.

Aby to zademonstrować, zobacz ten przykład:

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)

Wyjście (wypróbuj na 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]

Stworzyliśmy tablicę „podkładową” ao długości 10. Następnie tworzymy xdocelowy plasterek poprzez wycięcie tej atablicy, yplasterek jest tworzony przy użyciu literału złożonego []int{3, 4}. Teraz, kiedy dołączy ydo x, wynik jest oczekiwany [1 2 3 4], ale co może być zaskakujące jest to, że tablica podkład arównież zmieniona, ponieważ pojemność xIs 10która jest wystarczająca, aby dołączyć ydo niego, więc xjest resliced które również korzystają z tego samego atablicę podkładową, a append()skopiuje tam elementy y.

Jeśli chcesz tego uniknąć, możesz użyć wyrażenia pełnego wycinka, które ma formę

a[low : high : max]

który konstruuje plasterek, a także kontroluje pojemność wynikowego plastra, ustawiając go na max - low.

Zobacz zmodyfikowany przykład (jedyną różnicą jest to, że tworzymy w xten sposób 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)

Wyjście (wypróbuj na 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]

Jak widać, otrzymujemy ten sam xwynik, ale tablica podkładu asię nie zmieniła, ponieważ pojemność xbyła „tylko” 2(dzięki wyrażeniu pełnego wycinka a[:2:2]). Aby wykonać dołączenie, przydzielana jest nowa tablica podkładu, która może przechowywać elementy obu xi y, która jest inna a.

icza
źródło
2
Jest to bardzo pomocne w rozwiązywaniu problemu. Dzięki.
Aidy
9

Chciałbym podkreślić odpowiedź @icza i nieco ją uprościć, ponieważ jest to kluczowa koncepcja. Zakładam, że czytelnik zna plastry .

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

To jest prawidłowa odpowiedź na pytanie. ALE jeśli później trzeba użyć wycinków „a” i „c” w innym kodzie, nie jest to bezpieczny sposób łączenia wycinków.

Aby to wyjaśnić, przeczytajmy wyrażenie nie w kategoriach plasterków, ale w odniesieniu do podstawowych tablic:

„Weź (podstawową) tablicę„ a ”i dołącz do niej elementy z tablicy„ b ”. Jeśli tablica„ a ”ma wystarczającą pojemność, aby uwzględnić wszystkie elementy z„ b ”- podstawowa tablica„ c ”nie będzie nową tablicą , faktycznie będzie to tablica „a”. Zasadniczo plasterek „a” pokaże elementy len (a) elementów leżących u podstaw tablicy „a”, a plasterek „c” pokaże len (c) tablicy „a”.

append () niekoniecznie tworzy nową tablicę! Może to prowadzić do nieoczekiwanych rezultatów. Zobacz przykład Go Playground .

Zawsze używaj funkcji make (), jeśli chcesz mieć pewność, że dla wycinka przydzielona jest nowa tablica. Na przykład tutaj jest kilka brzydkich, ale wystarczająco wydajnych opcji do zadania.

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)
DC Joo
źródło
Dziękujemy za wskazanie tych działań niepożądanych. Zadziwiająco kontrastuje z tym zmodyfikowanym szenario. play.golang.org/p/9FKo5idLBj4 Chociaż zapewniając nadwyżkę pojemności, należy dokładnie przemyśleć te zagadkowe skutki uboczne wbrew wiarygodnej intuicji.
olippuner
5

funkcja append () i operator rozkładania

Dwa wycinki mogą być łączone przy użyciu appendmetody ze standardowej biblioteki golang. Który jest podobny do działania variadicfunkcji. Więc musimy użyć...

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

wyjście powyższego kodu to: [1 2 3 4 5 6]

ASHWIN RAJEEV
źródło
2

append([]int{1,2}, []int{3,4}...)będzie działać. Przekazywanie argumentów do ...parametrów.

Jeśli fjest variadic z końcowym parametrem ptypu ...T, to wewnątrz ftypu pjest równoważne typowi []T.

Jeśli fzostanie wywołany bez rzeczywistych argumentów p, przekazana wartość pto nil.

W przeciwnym razie przekazana wartość jest nowym wycinkiem typu []Tz nową podstawową tablicą, której kolejnymi elementami są rzeczywiste argumenty, do których wszystkich należy przypisać T. Długość i pojemność wycinka jest zatem liczbą powiązanych argumentów pi może się różnić dla każdej witryny wywoływania.

Biorąc pod uwagę funkcję i wywołania

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
BaSO4
źródło