Gabungkan dua irisan di Go


478

Saya mencoba menggabungkan irisan [1, 2]dan irisan [3, 4]. Bagaimana saya bisa melakukan ini di Go?

Saya mencoba:

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

tapi punya:

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

Namun, dokumentasi sepertinya mengindikasikan ini mungkin, apa yang saya lewatkan?

slice = append(slice, anotherSlice...)

Jawaban:


879

Tambahkan titik setelah irisan kedua:

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

Ini sama seperti fungsi variadik lainnya.

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()fungsi variadic, dan ...memungkinkan Anda melewati beberapa argumen ke fungsi variadic dari slice.

11
Apakah ini sama sekali pemain ketika irisan cukup besar? Atau apakah kompiler tidak benar-benar lulus semua elemen sebagai parameter?
Kodok

15
@ Memuat: Itu tidak benar-benar menyebarkannya. Dalam foo()contoh di atas, isparameter menyimpan salinan irisan asli, yang artinya memiliki salinan referensi ringan ke array, len dan tutup yang sama yang mendasarinya. Jika foofungsi mengubah anggota, perubahan akan terlihat pada aslinya. Ini demo . Jadi, satu-satunya overhead nyata adalah membuat irisan baru jika Anda belum memilikinya, seperti: foo(1, 2, 3, 4, 5)yang akan membuat irisan baru yang isakan menampung.

2
Ah. Jika saya mengerti dengan benar, fungsi variadic sebenarnya diimplementasikan seperti array parameter (bukan setiap parameter pada stack)? Dan karena Anda melewati irisan, itu benar-benar memetakan satu lawan satu?
Kodok

@ Memuat: Ya, ketika Anda menggunakan ...pada irisan yang ada, itu hanya melewati irisan itu. Ketika Anda melewati argumen individual, itu mengumpulkan mereka ke dalam irisan baru dan meneruskannya. Saya tidak memiliki pengetahuan tangan pertama dari mekanisme yang tepat, tapi saya akan menebak bahwa ini: foo(1, 2, 3, 4, 5)dan ini: func foo(is ...int) {hanya de-gula ini: foo([]int{1, 2, 3, 4, 5})dan ini: func foo(is []int) {.

77

Menambahkan dan menyalin irisan

Fungsi variadic appendmenambahkan nol atau lebih nilai xuntuk s jenis S, yang harus menjadi tipe slice, dan mengembalikan slice yang dihasilkan, juga jenis S. Nilai xditeruskan ke parameter tipe di ...Tmana Ttipe elemen Sdan aturan parameter lewat masing-masing berlaku. Sebagai kasus khusus, append juga menerima argumen pertama yang ditugaskan untuk mengetik []bytedengan argumen stringtipe kedua diikuti oleh .... Formulir ini menambahkan byte dari string.

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}

Melewati argumen ke ... parameter

Jika fvariadik dengan tipe parameter akhir ...T, maka dalam fungsi argumennya setara dengan parameter tipe []T. Pada setiap pemanggilan f, argumen yang diteruskan ke parameter terakhir adalah potongan tipe baru []Tyang elemen-elemen berikutnya adalah argumen aktual, yang semuanya harus dapat ditetapkan untuk tipe tersebut T. Karena itu panjang irisan adalah jumlah argumen yang terikat pada parameter akhir dan mungkin berbeda untuk setiap situs panggilan.

Jawaban untuk pertanyaan Anda adalah contoh s3 := append(s2, s0...)dalam Spesifikasi Bahasa Pemrograman Go . Sebagai contoh,

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

6
Catatan: penggunaan umum append (slice1, slice2 ...) tampaknya cukup berbahaya bagi saya. Jika slice1 adalah irisan array yang lebih besar, nilai-nilai array itu akan ditimpa oleh slice2. (Itu membuat saya ngeri bahwa ini tampaknya tidak menjadi perhatian bersama?)
Hugo

7
@Hugo Jika Anda "menyerahkan" sepotong array Anda, maka ketahuilah bahwa slice "pemilik" akan dapat melihat / menimpa bagian-bagian array yang berada di luar panjang slice saat ini. Jika Anda tidak menginginkan ini, Anda dapat menggunakan ekspresi irisan penuh (dalam bentuk a[low : high : max]) yang juga menentukan kapasitas maksimum . Sebagai contoh, slice a[0:2:4]akan memiliki kapasitas 4dan tidak dapat disambung ulang untuk memasukkan elemen di luar itu, bahkan jika array backing memiliki seribu elemen setelahnya.
icza

30

Tidak ada yang bertentangan dengan jawaban lain, tetapi saya menemukan penjelasan singkat di dokumen lebih mudah dimengerti daripada contoh di dalamnya:

func menambahkan

func append(slice []Type, elems ...Type) []TypeFungsi built-in append menambahkan elemen ke akhir slice. Jika memiliki kapasitas yang memadai, tujuan disatukan untuk mengakomodasi elemen-elemen baru. Jika tidak, array dasar yang baru akan dialokasikan. Tambahkan mengembalikan potongan yang diperbarui. Oleh karena itu perlu untuk menyimpan hasil append, sering dalam variabel yang memegang slice itu sendiri:

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

Sebagai kasus khusus, adalah sah untuk menambahkan string ke byte byte, seperti ini:

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

1
Terima kasih! Berharga buat saya!
Korjavin Ivan

23

Saya pikir sangat penting untuk menunjukkan dan mengetahui bahwa jika irisan tujuan (irisan yang Anda tambahkan) memiliki kapasitas yang cukup, append akan terjadi "di tempat", dengan reslicing tujuan (reslicing untuk menambah panjangnya agar menjadi mampu mengakomodasi elemen-elemen yang dapat ditambahkan).

Ini berarti bahwa jika tujuan dibuat dengan mengiris array atau irisan yang lebih besar yang memiliki elemen tambahan di luar panjang irisan yang dihasilkan, mereka mungkin akan ditimpa.

Untuk menunjukkan, lihat contoh ini:

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)

Output (coba di 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]

Kami membuat array "dukungan" adengan panjang 10. Kemudian kita membuat xirisan tujuan dengan mengiris aarray ini , yirisan dibuat menggunakan literal komposit []int{3, 4}. Sekarang ketika kita menambahkan yuntuk x, hasilnya adalah diharapkan [1 2 3 4], tapi apa yang mungkin mengejutkan adalah bahwa array dukungan ajuga berubah, karena kapasitas xyaitu 10yang cukup untuk menambahkan yuntuk itu, jadi xini resliced yang juga akan menggunakan yang sama aberbagai dukungan, dan append()akan menyalin elemen yke sana.

Jika Anda ingin menghindari ini, Anda dapat menggunakan ekspresi irisan penuh yang memiliki formulir

a[low : high : max]

yang membangun slice dan juga mengontrol kapasitas slice yang dihasilkan dengan mengaturnya max - low.

Lihat contoh yang dimodifikasi (satu-satunya perbedaan yang kita buat xseperti ini 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)

Output (coba di 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]

Seperti yang Anda lihat, kami mendapatkan xhasil yang sama tetapi array backing atidak berubah, karena kapasitas x"hanya" 2(terima kasih kepada ekspresi irisan penuh a[:2:2]). Jadi untuk melakukan append, array backing baru dialokasikan yang dapat menyimpan elemen keduanya xdan y, yang berbeda dari a.


2
Ini sangat membantu untuk masalah yang saya hadapi. Terima kasih.
Aidy

9

Saya ingin menekankan jawaban @icza dan menyederhanakannya karena ini adalah konsep yang sangat penting. Saya berasumsi bahwa pembaca sudah terbiasa dengan irisan .

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

Ini adalah jawaban yang valid untuk pertanyaan itu. TETAPI jika Anda perlu menggunakan irisan 'a' dan 'c' nanti dalam kode dalam konteks yang berbeda, ini bukan cara yang aman untuk menggabungkan irisan.

Untuk menjelaskan, mari kita baca ekspresi bukan dalam hal irisan, tetapi dalam hal array yang mendasarinya:

"Ambil (mendasari) array 'a' dan tambahkan elemen dari array 'b' ke sana. Jika array 'a' memiliki kapasitas yang cukup untuk memasukkan semua elemen dari 'b' - array yang mendasari 'c' tidak akan menjadi array baru , sebenarnya akan menjadi array 'a'. Pada dasarnya, slice 'a' akan menunjukkan len (a) elemen dari array yang mendasari 'a', dan slice 'c' akan menunjukkan len (c) dari array 'a'. "

append () tidak harus membuat array baru! Ini dapat menyebabkan hasil yang tidak terduga. Lihat contoh Go Playground .

Selalu gunakan fungsi make () jika Anda ingin memastikan bahwa array baru dialokasikan untuk slice. Misalnya di sini ada beberapa opsi yang jelek tapi cukup efisien untuk tugas tersebut.

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)

Terima kasih telah menunjukkan efek samping ini. Sangat berbeda dengan szenario yang dimodifikasi ini. play.golang.org/p/9FKo5idLBj4 Meskipun saat memberikan kapasitas berlebih, kita harus hati-hati memikirkan efek samping yang membingungkan ini terhadap intuisi yang masuk akal.
olippuner

5

append () berfungsi dan operator spread

Dua irisan dapat digabungkan menggunakan appendmetode di perpustakaan standar golang. Yang mirip dengan variadicoperasi fungsi. Jadi kita perlu menggunakan...

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

output dari kode di atas adalah: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)akan bekerja. Melewati argumen ke ...parameter.

Jika fvariadic dengan parameter ptipe terakhir ...T, maka di fdalam tipe pitu sama dengan type []T.

Jika fdipanggil tanpa argumen aktual untuk p, nilai yang diteruskan padalah nil.

Jika tidak, nilai yang diteruskan adalah irisan tipe []Tbaru dengan larik dasar baru yang elemen berurutannya adalah argumen aktual, yang harus ditetapkan oleh semua orang T. Panjang dan kapasitas irisan karena itu adalah jumlah argumen yang terikat pdan mungkin berbeda untuk setiap situs panggilan.

Diberi fungsi dan panggilan

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.