Go公式WikiのSliceTricksの日本語意訳になります。
(元記事の最終更新日: 2017/06/29 rev.23)
スライスを扱う上で有用なテクニックがまとまっています。
スライス・トリック
container/vector
パッケージの機能はGoのバージョンが1になり削除されましたが、代わりに組み込みのappend
と copy
に置き換えられました。
ベクトルとスライス操作は以下のようになります。
ベクトルの追加
a = append(a, b...)
コピー
b = make([]T, len(a))
copy(b, a)
// または以下の方法でも可
b = append([]T(nil), a...)
部分カット
a = append(a[:i], a[j:]...)
訳注: 例えばスライスの先頭の要素10個と末尾の要素10個を取得したい場合は以下のようになります
Play Groundで確認
size := len(a)
a = append(a[:10], a[(size-10):]...)
削除
a = append(a[:i], a[i+1:]...)
// または以下の方法でも可
a = a[:i+copy(a[i:], a[i+1:])]
削除(元の順番を保証しない)
a[i] = a[len(a)-1]
a = a[:len(a)-1]
~注~ もし要素の型がポインタ型やポインタのフィールドを含む構造体の場合、ガベージコレクション(GC)が必要になるため、上記の部分カットや削除の実装は潜在的なメモリリークの問題があります。値を持つ要素はスライスからの参照が残っており、GCの対象になりません。この問題を修正したコードは以下になります。
部分カット
copy(a[i:], a[j:])
for k, n := len(a)-j+i, len(a); k < n; k++ {
a[k] = nil // nil or この型のゼロ値を代入する
}
a = a[:len(a)-j+i]
削除
copy(a[i:], a[i+1:])
a[len(a)-1] = nil // nil or この型のゼロ値を代入する
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:]...)...)
~注~ 2番目の append
は、自身の領域を持つ新しいスライスを作成し、その新しいスライスにa[i:]
の要素をコピーします。そしてこれらの要素は(1番目の append
で) 元のスライス a
へとコピーされます。新しいスライスの作成(とそれによるメモリガベージ)と2番目のコピーは他の方法で回避することもできます。
挿入
s = append(s, 0)
copy(s[i+1:], s[i:])
s[i] = x
ベクトルの挿入
a = append(a[:i], append(b, a[i:]...)...)
Pop(末尾からの取出し)
x, a = a[len(a)-1], a[:len(a)-1]
Shift, Pop Back(先頭からの取出し)
x, a = a[0], a[1:]
Push(末尾への追加)
a = append(a, x)
Unshift, Push Back(先頭への追加)
a = append([]T{x}, a...)
応用トリック
メモリ割り当てをせずにフィルタリングする
このトリックは、スライスがその背後にある配列と元の容量(capacity
)を共有するという事実を使い、その領域はフィルタ済みスライスによって再利用されます。もちろん、元の内容は変更されます。
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]
}
comments powered by Disqus