Goで2つのスライスを連結する


477

私はスライス[1, 2]とスライスを結合しようとしています[3, 4]。Goでこれを行うにはどうすればよいですか?

私は試した:

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

しかし得た:

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

しかし、ドキュメントにはこれが可能であることが示されているようですが、何が欠けていますか?

slice = append(slice, anotherSlice...)

回答:


878

2番目のスライスの後にドットを追加します。

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

これは、他の可変関数と同じです。

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()可変個関数と、...スライスから可変個個関数に複数の引数を渡すことができます。

11
スライスが非常に大きい場合、これはまったくパフォーマンスが良いですか?または、コンパイラは実際にはすべての要素をパラメータとして渡しませんか?
ヒキガエル

15
@ヒキガエル:それは実際にそれらを広げない。foo()上記の例では、isパラメーターは元のスライスのコピーを保持しています。つまり、同じ基本的な配列lenおよびcapへの軽量参照のコピーを持っています。foo関数がメンバーを変更した場合、変更は元のメンバーに表示されます。こちらがデモです。したがって、実際のオーバーヘッドは、スライスがまだない場合に新しいスライスfoo(1, 2, 3, 4, 5)を作成isすることだけです。

2
ああ。私が正しく理解していれば、可変要素関数は実際には(スタック上のすべてのパラメーターではなく)パラメーターの配列のように実装されていますか?そして、スライスを渡すので、実際には1対1でマッピングしますか?
ヒキガエル

@Toad:はい、...既存のスライスで使用する場合、単にそのスライスを渡します。個々の引数を渡すと、それらは新しいスライスにまとめられて渡されます。私は、正確な力学の最初の手の知識を持っていないが、私は推測するだろう、この:foo(1, 2, 3, 4, 5)この:func foo(is ...int) {これまでちょうど解除糖:foo([]int{1, 2, 3, 4, 5})この:func foo(is []int) {

77

スライスへの追加とコピー

可変個引数関数appendは0個以上の値xs typeの値に追加しますS。これはスライス型である必要があり、結果のスライスもtypeで返しますS。値xはのタイプのパラメーターに渡されます。...TここTで、はのエレメントタイプでSあり、それぞれのパラメーター渡しルールが適用されます。特殊なケースとして、appendは、typeに割り当て可能な最初の引数を受け入れ、type []byteの2番目の引数の 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}

...パラメータに引数を渡す

fが最終的なパラメーターの型を持つ可変個の場合...T、関数内では、引数は型のパラメーターと同等[]Tです。の各呼び出しでf、最後のパラメータに渡される引数は、[]T連続する要素が実際の引数であるタイプの新しいスライスであり、すべてがタイプに割り当て可能でなければなりませんT。したがって、スライスの長さは最後のパラメーターにバインドされた引数の数であり、呼び出しサイトごとに異なる場合があります。

あなたの質問に対する答えs3 := append(s2, s0...)は、Goプログラミング言語仕様の例です。例えば、

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

6
注:append(slice1、slice2 ...)の一般的な使用は、私にとって非常に危険に思えます。slice1がより大きな配列のスライスである場合、その配列の値はslice2によって上書きされます。(これが共通の懸念事項ではないように思われるので、私はうんざりしていますか?)
Hugo

7
@Hugo配列のスライスを「引き渡す」場合、スライスの「所有者」が現在のスライスの長さを超える配列の部分を表示/上書きできることを知ってください。これが必要ない場合は、最大容量も指定する完全なスライス式(の形式a[low : high : max])を使用できます。たとえば、スライスにはの容量があり、それを超える要素を含めるように再スライスすることはできません。その後にバッキングアレイに1000の要素がある場合でも同様です。a[0:2:4]4
icza

30

他の回答には何もありませんが、ドキュメントの簡単な説明はそれらの例よりも簡単に理解できることがわかりました。

func append

func append(slice []Type, elems ...Type) []Type追加組み込み関数は、要素をスライスの最後に追加します。十分な容量がある場合、宛先は新しい要素に対応するように再割り当てされます。そうでない場合は、新しい基本配列が割り当てられます。Appendは更新されたスライスを返します。したがって、多くの場合、スライス自体を保持する変数に追加の結果を格納する必要があります。

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

特殊なケースとして、次のようにバイトスライスに文字列を追加することは合法です。

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

1
ありがとうございました!私にとって貴重です!
Korjavin Ivan

23

宛先スライス(追加先のスライス)に十分な容量がある場合、宛先を再スライスすることによって(追加するためにその長さを増やすように再スライスすることにより)追加が「インプレース」で行われることを指摘し、知っておくことが重要だと思います追加可能な要素に対応できます)。

これは、結果のスライスの長さを超える追加の要素がある大きな配列またはスライスをスライスすることによって宛先が作成された場合、それらが上書きされる可能性があることを意味します。

例として、次の例をご覧ください。

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)

出力(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]

a長さの「バッキング」配列を作成しました10。次に、xこのa配列をスライスして宛先スライスを作成します。yスライスは、複合リテラルを使用して作成され[]int{3, 4}ます。ここでに追加yするxと、結果は期待どおりですが、[1 2 3 4]驚くべきことは、バッキング配列aも変更されたことxです。10これは、容量がis に追加yするのに十分なため、バッキング配列も変更され、x同じaバッキング配列も使用されるので、append()の要素yをそこにコピーします。

これを避けたい場合は、次の形式の完全なスライス式を使用できます。

a[low : high : max]

これはスライスを作成し、に設定することにより、結果のスライスの容量も制御しmax - lowます。

変更された例を参照してください(唯一の違いは、次のxように作成することです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)

出力(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]

ご覧のように、同じx結果が得られますが、バッキング配列aは変更されませんでした。これは、容量xが「唯一」だったためです2(完全なスライス式のおかげですa[:2:2])。したがって、追加を行うために、とは異なる、xおよびの両方の要素を格納できる新しいバッキング配列が割り当てられます。ya


2
それは私が直面している問題に非常に役立ちます。ありがとう。
Aidy

9

@iczaの回答は重要な概念なので、@ iczaの回答を強調し、少し簡略化したいと思います。読者はスライスに慣れていると思います。

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

これは質問に対する有効な回答です。 しかし、後で別のコンテキストでコードのスライス「a」と「c」を使用する必要がある場合、これはスライスを連結する安全な方法ではありません。

説明するために、スライスではなく、基礎となる配列の観点から式を読み取ってみましょう。

「 'a'の(基礎となる)配列を取得し、それに配列 'b'の要素を追加します。配列 'a'に 'b'のすべての要素を含めるのに十分な容量がある場合-'c'の基になる配列は新しい配列にはなりません、実際には配列 'a'になります。基本的に、スライス 'a'は基礎となる配列 'a'のlen(a)要素を表示し、スライス 'c'は配列 'a'のlen(c)を表示します。

append()は必ずしも新しい配列を作成するわけではありません!これにより、予期しない結果が生じる可能性があります。Go Playgroundの例を参照してください。

スライスに新しい配列が割り当てられていることを確認したい場合は、常にmake()関数を使用してください。たとえば、ここではいくつかの醜いが効率的なタスクのオプションがあります。

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)

これらの副作用を指摘していただきありがとうございます。この修正されたシナリオと驚くほど対照的です。play.golang.org/p/9FKo5idLBj4ただし、過剰な容量を提供する場合は、もっともらしい直感に対するこれらの不可解な副作用について慎重に検討する必要があります。
olippuner

5

append()関数とスプレッド演算子

2つのスライスはappend、標準のgolangライブラリのメソッドを使用して連結できます。これはvariadic関数操作に似ています。したがって、使用する必要があります...

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

上記のコードの出力は次のとおりです:[1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)働くでしょう。...パラメータに引数を渡します。

がtypeのf最終パラメーターpを持つ可変個の場合、の型...Tfはtype pと同等[]Tです。

fがの実際の引数なしで呼び出された場合p、渡される値pnilです。

それ以外の場合、渡される値は、[]T連続する要素が実際の引数である新しい基本配列を持つタイプの新しいスライスであり、すべてがに割り当て可能でなければなりませんT。したがって、スライスの長さと容量は、バインドされる引数の数であり、p呼び出しサイトごとに異なる場合があります。

関数と呼び出しを考える

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.