web-dev-qa-db-ja.com

GO langの要素を削除して配列に追加する

次のように宣言された2つの配列があります:var input []stringおよびvar output []string

入力配列には、最初にいくつかのIDが入力されます。出力配列はNULLです。

繰り返しのたびに、入力配列からランダムな要素を削除して、出力配列に追加します。

最後に、出力配列のすべての要素は入力配列と同じになります(ただし、順序付け(インデックス付け)は異なります)。

for index := 0; index < len(input); index++ {
    if !visited[index] {
        //do something
    }
}
output[#iteration index] = input[current index]

これを実行しようとすると、array out of bounds error

17
fnaticRC ggwp

output配列の場合、appendを使用するか、inputのサイズに合わせて初期容量を割り当てる必要があります。

// before the loop
output := make([]string, len(input))

appendが不必要な再割り当てを引き起こし、inputに基づいているために必要な容量を既に知っているので、私の推奨事項です。

他のことは次のとおりです。

output = append(output, input[index])

しかし、私が言ったように、私が観察したことから、appendは初期容量を指数関数的に増加させます。何も指定していない場合、これはベース2になります。つまり、目的の容量に達する前に、不必要な再割り当てをいくつか行うことになります。

22
evanmcdonnal

golang/SliceTricks で便利なトリックを見つけることができます。

appendビルトインの導入以来、Go 1で削除されたcontainer/vectorパッケージのほとんどの機能は、appendおよびcopyを使用して複製できます。

ベクトルメソッドとそのスライス操作アナログは次のとおりです。

a = append(a, b...)
b = make([]T, len(a))
copy(b, a)
// or
b = append([]T(nil), a...)
a = append(a[:i], a[j:]...)
a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]
a[i] = a[len(a)-1] 
a = a[:len(a)-1]

[〜#〜] note [〜#〜]要素の型がpointerの場合または、ガベージコレクションが必要なポインタフィールドを持つ構造体、上記のCutおよびDeleteの実装には、潜在的なメモリリーク問題があります。一部の要素with valueはスライスaによって引き続き参照されるため、収集できません。次のコードでこの問題を修正できます。

カット

copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
    a[k] = nil // or the zero value of T
}
a = a[:len(a)-j+i]

削除

copy(a[i:], a[i+1:])
a[len(a)-1] = nil // or the zero value of T
a = a[:len(a)-1]

順序を維持せずに削除する

a[i] = a[len(a)-1]
a[len(a)-1] = nil
a = a[:len(a)-1]
a = append(a[:i], append(make([]T, j), a[i:]...)...)
a = append(a, make([]T, j)...)
a = append(a[:i], append([]T{x}, a[i:]...)...)

[〜#〜] note [〜#〜]2番目のappendは、独自のストレージを使用して新しいスライスを作成し、a[i:]の要素をコピーしますそのスライスにコピーされ、これらの要素はスライスaにコピーされます(最初のappendによって)。別の方法を使用することにより、新しいスライス(およびメモリガベージ)と2番目のコピーの作成を回避できます。

挿入

s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x
a = append(a[:i], append(b, a[i:]...)...)
x, a = a[0], a[1:]
x, a = a[len(a)-1], a[:len(a)-1]
a = append(a, x)
a = append([]T{ x }, a...)
x, a := a[0], a[1:]
a = append([]T{x}, a...)

追加のコツ

割り当てずにフィルタリングする

このトリックは、スライスが元のバッキングアレイと同じバッキングアレイと容量を共有するという事実を使用するため、ストレージはフィルター処理されたスライスに再利用されます。もちろん、元のコンテンツは変更されます。

b := a[:0]
for _, x := range a {
    if f(x) {
        b = append(b, x)
    }
}

逆転

スライスのコンテンツを同じ要素で逆の順序に置き換えるには:

for i := len(a)/2-1; i >= 0; i-- {
    opp := len(a)-1-i
    a[i], a[opp] = a[opp], a[i]
}

2つのインデックスを除いて同じこと:

for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 {
    a[left], a[right] = a[right], a[left]
}

シャッフリング

Fisher–Yatesアルゴリズム:

for i := len(a) - 1; i > 0; i-- {
    j := Rand.Intn(i + 1)
    a[i], a[j] = a[j], a[i]
}
5
TonnyL