Go'da iki dilimi birleştirin


477

Ben dilim [1, 2]ve dilim birleştirmeye çalışıyorum [3, 4]. Bunu Go'da nasıl yapabilirim?

Denedim:

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

ama var:

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

Ancak, belgeler bunun mümkün olduğunu gösteriyor, ne eksik?

slice = append(slice, anotherSlice...)

Yanıtlar:


877

İkinci dilimden sonra noktalar ekleyin:

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

Bu, diğer varyasyon işlevleri gibi.

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()variadic fonksiyon ve ...bir dilim gelen variadic çalışması için birden fazla argüman geçmemize izin verir.

11
Dilimler oldukça büyük olduğunda bu hiç performans göstermiyor mu? Veya derleyici gerçekten tüm öğeleri parametre olarak geçirmiyor mu?
Kurbağa

15
@Toad: Aslında onları yaymıyor. İçinde foo()yukarıdaki örnekte, isparametre aynı temel len dizi ve kapağa hafif referans bir kopyası olduğunu söylemek orijinal dilim, bir kopyasını tutar. Eğer foofonksiyon kişiliklerinden üyesi değişim orijinal üzerinde görülecektir. İşte bir demo . Yani tek asıl yük, daha önce yoksa, foo(1, 2, 3, 4, 5)yeni bir dilim oluşturmasıdır : istutacak yeni bir dilim oluşturacaktır .

2
Ah. Doğru anlıyorsam, varyasyon fonksiyonu aslında bir parametre dizisi gibi (yığın üzerindeki her parametre yerine) uygulanır? Ve dilimi geçtiğiniz için, aslında bire bir harita?
Kurbağa

@Toad: Evet, ...mevcut bir dilimi kullandığınızda, o dilimi geçer. Bireysel argümanları ilettiğinizde, bunları yeni bir dilime toplar ve iletir. Ben tam mekaniği birinci elden bilgi yok, ama tahmin ediyorum bu: foo(1, 2, 3, 4, 5)ve bu: func foo(is ...int) {bu sadece de-şekerler: foo([]int{1, 2, 3, 4, 5})ve bu: func foo(is []int) {.

77

Dilimlere ekleme ve kopyalama

Değişkin fonksiyonu appendsıfır ya da daha fazla değeri ekler xiçin s Çeşidi Sbir dilim türü olmalıdır, ve ayrıca Çeşidi, elde edilen dilim döndürür S. Değerler , eleman tipinin ve ilgili parametre geçiş kurallarının geçerli olduğu xtipteki bir parametreye iletilir . Özel bir durum olarak, komuta da yazmak için bir birinci bağımsız değişken atanabilir kabul ikinci bir bağımsız değişken ile , ardından türü . Bu form dizenin baytlarını ekler....TTS[]bytestring...

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}

... parametrelerine argümanlar iletiliyor

Eğer fson parametre tipi değişkin olup ...T, daha sonra işlev içinde bağımsız değişken tipte bir parametre eşdeğerdir []T. Her çağrısında f, son parametreye iletilen bağımsız değişken []T, ardışık öğeleri gerçek bağımsız değişkenler olan ve tümü türe atanması gereken yeni bir tür dilimdir T. Bu nedenle dilimin uzunluğu, son parametreye bağlı olan bağımsız değişkenlerin sayısıdır ve her çağrı sitesi için farklı olabilir.

Sorunuzun cevabı örneğidir s3 := append(s2, s0...)içinde git Programlama Dili Şartname . Örneğin,

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

6
Not: ekin (dilim1, dilim2 ...) genel kullanımı benim için oldukça tehlikeli görünüyor. Dilim1 daha büyük bir dizinin dilimiyse, o dizinin değerlerinin üzerine dilim2 yazılır. (Bu ortak bir endişe gibi görünmüyor beni kandırıyor?)
Hugo

7
@Hugo Dizinizin bir dilimine "el" verirseniz, "sahibi" diliminin dizinin geçerli uzunluğunun ötesindeki bölümlerini görebileceğini / üzerine yazabileceğini bilin. Bunu istemiyorsanız , maksimum kapasiteyi de belirten tam bir dilim ifadesi (şeklinde a[low : high : max]) kullanabilirsiniz . Örneğin, dilimin kapasitesi olacaktır ve destek dizisi bundan sonra bin öğeye sahip olmasa bile, bunun ötesindeki öğeleri içermek için yeniden tasarlanamaz. a[0:2:4]4
icza

30

Diğer cevaplara karşı hiçbir şey yok, ancak dokümanlardaki kısa açıklamayı , bunlardaki örneklerden daha kolay anlaşılır buldum :

func append

func append(slice []Type, elems ...Type) []TypeYerleşik ekle işlevi, bir dilimin sonuna öğeleri ekler. Yeterli kapasiteye sahipse, varış yeri yeni unsurları barındıracak şekilde yeniden oluşturulur. Başlamazsa, yeni bir temel dizi atanacaktır. Ekle, güncellenmiş dilimi döndürür. Bu nedenle, ekin sonucunu, genellikle dilimin kendisini tutan değişkente saklamak gerekir:

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

Özel bir durum olarak, bayt dilimine bir dize eklemek yasaldır:

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

1
Teşekkür ederim! Benim için değerli!
Korjavin Ivan

23

Ben işaret ve hedef dilim (eklediğiniz dilim) yeterli kapasiteye sahip olduğunu bilmek önemli olduğunu düşünüyorum, eklenti "yerinde", hedefini yeniden (olması için uzunluğunu artırmak için reslicing) olacak ek öğeleri barındırabilir).

Bu, hedefin, elde edilen dilimin uzunluğunun ötesinde ek öğelere sahip daha büyük bir dizi veya dilim dilimlenerek oluşturulduğu takdirde, bunların üzerine yazılabilir.

Göstermek için şu örneğe bakın:

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)

Çıktı ( Go Playground'da deneyin ):

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]

aUzunluğunda bir "destek" dizisi oluşturduk 10. Sonra xbu adiziyi dilimleyerek hedef dilimi yaratırız , ydilim bileşik değişmezi kullanılarak oluşturulur []int{3, 4}. Biz eklemek Şimdi ne zaman yiçin x, sonuç beklenen olduğu [1 2 3 4], ama ne şaşırtıcı olabilir destek dizisi olmasıdır akapasitesi nedeniyle de değişmiş xIS 10yeterlidir eklemek için ykendisine, bu nedenle xde aynı kullanacağı resliced olan asırt dizi ve append()öğelerini yoraya kopyalar .

Bundan kaçınmak istiyorsanız , forma sahip tam bir dilim ifadesi kullanabilirsiniz.

a[low : high : max]

bu da bir dilim oluşturur ve ayrıca elde edilen dilimin kapasitesini ayarlayarak kontrol eder max - low.

Modifiye örneğe bakın (tek fark yarattığımız olmasıdır xböyle: 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)

Çıktı ( Go Playground'da deneyin )

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]

Gördüğünüz gibi, aynı xsonucu alıyoruz, ancak yedekleme dizisi adeğişmedi, çünkü kapasitesi x"sadece" idi 2(tam dilim ifadesi sayesinde a[:2:2]). Bu nedenle, eki yapmak için, her ikisinin öğelerini saklayabilen xve yfarklı olan yeni bir destek dizisi ayrılır a.


2
Karşılaştığım soruna çok yardımcı oluyor. Teşekkürler.
Aidy

9

@İcza cevabını vurgulamak ve çok basit bir kavram olduğu için biraz basitleştirmek istiyorum. Okuyucunun dilimlere aşina olduğunu varsayıyorum .

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

Bu soruya geçerli bir cevaptır. AMA daha sonra farklı bağlamlarda 'a' ve 'c' dilimlerini kullanmanız gerekiyorsa, dilimleri birleştirmenin güvenli bir yolu bu değildir.

Açıklamak için, ifadeyi dilimler açısından değil, alttaki diziler açısından okuyalım:

"'A' dizisinin (altta yatan) dizisini alın ve 'b' dizisinden öğeleri ekleyin. 'A' dizisi, 'b' öğesinin tüm öğelerini dahil etmek için yeterli kapasiteye sahipse - 'c' temel dizisi yeni bir dizi olmayacak aslında 'a' dizisi olacaktır. Temel olarak, 'a' dilimi altta 'a' dizisinin len (a) öğelerini gösterecek ve 'c' dilimi 'a' dizisinin len (c) 'sini gösterecektir.

append () mutlaka yeni bir dizi oluşturmaz! Bu beklenmedik sonuçlara yol açabilir. Go Playground örneğine bakın .

Dilim için yeni dizinin ayrıldığından emin olmak istiyorsanız her zaman make () işlevini kullanın. Örneğin, burada görev için yeterince çirkin ama yeterince etkili seçenek var.

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)

Bu yan etkilere işaret ettiğiniz için teşekkür ederiz. Bu değiştirilmiş szenario ile şaşırtıcı bir zıtlık. play.golang.org/p/9FKo5idLBj4 Fazla kapasite sağlarken, makul sezgiye karşı bu şaşırtıcı yan etkileri dikkatlice düşünmek gerekir.
olippuner

5

append () işlevi ve yayılma işleci

appendStandart golang kütüphanesindeki yöntem kullanılarak iki dilim birleştirilebilir . Hangi variadicişlev işlemine benzer . Bu yüzden kullanmalıyız...

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

Yukarıdaki kodun çıktısı: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)çalışacak. Değişkenlerin ...parametrelere iletilmesi .

Eğer fnihai parametresiyle değişkin olan pÇeşidi ...Tsonra içinde ftipi ptip eşdeğerdir []T.

İçin fgerçek bağımsız değişken olmadan çağrılırsa p, iletilen değer ' pdir nil.

Aksi takdirde, iletilen değer []T, ardışık öğeleri asıl bağımsız değişkenler olan ve hepsinin atanması gereken yeni bir temel diziye sahip yeni bir tür dilimdir T. Dilimin uzunluğu ve kapasitesi, bu nedenle, pher çağrı sitesine bağlı olan bağımsız değişkenlerin sayısıdır ve farklı olabilir.

Fonksiyon ve çağrılar verildiğinde

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.