次のように宣言された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
。
output
配列の場合、append
を使用するか、input
のサイズに合わせて初期容量を割り当てる必要があります。
// before the loop
output := make([]string, len(input))
append
が不必要な再割り当てを引き起こし、input
に基づいているために必要な容量を既に知っているので、私の推奨事項です。
他のことは次のとおりです。
output = append(output, input[index])
しかし、私が言ったように、私が観察したことから、appendは初期容量を指数関数的に増加させます。何も指定していない場合、これはベース2になります。つまり、目的の容量に達する前に、不必要な再割り当てをいくつか行うことになります。
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]
}