Go 1.22でslicesパッケージに追加された便利関数
こんにちは。ついにGo 1.22がリリースされましたね!
1.22
のリリースではfor
の変更に注目が集まりがちですが、標準ライブラリであるslices
パッケージにも地味に便利な関数が追加されています。(Release note)
この記事では、今回slices
パッケージに追加されたConcat
、Delete
、Insert
を紹介します。
Concat
func Concat[S ~[]E, E any](slices ...S) S
Concat returns a new slice concatenating the passed in slices.
渡されたスライスを結合して、新たなスライスとして返します。
複数のスライスを1回で結合できる点が便利だと思いました。
今まで、複数のスライスを結合する場合は以下のように書いていました。
slice1 := []int64{1, 2}
slice2 := []int64{3, 4}
slice3 := []int64{5, 6}
slice4 := []int64{7, 8}
slice1 = append(slice1, slice2...)
slice1 = append(slice1, slice3...)
slice1 = append(slice1, slice4...)
// or slice1 = append(append(append(slice1, slice2...), slice3...), slice4...)
fmt.Print(slice1) // [1 2 3 4 5 6 7 8]
これをConcat
を使うと、以下のように書くことができます。
slice1 := []int64{1, 2}
slice2 := []int64{3, 4}
slice3 := []int64{5, 6}
slice4 := []int64{7, 8}
newSlice := slices.Concat(slice1, slice2, slice3, slice4)
fmt.Print(newSlice) // [1 2 3 4 5 6 7 8]
何度もappend
する必要がなくなり、スッキリと直感的に書けるようになりました!
個人的に、テストコードの中で何度もappend
したことがあったので、この関数の追加は嬉しいです。
Delete
func Delete[S ~[]E, E any](s S, i, j int) S
Delete removes the elements s[i:j] from s, returning the modified slice.
Delete panics if j > len(s) or s[i:j] is not a valid slice of s.
Delete is O(len(s)-i), so if many items must be deleted,
it is better to make a single call deleting them all together than to delete one at a time.
Delete zeroes the elements s[len(s)-(j-i):len(s)].
スライスからi:j
で指定された要素を削除し、変更されたスライスを返します。
指定されたj
がスライスの最大長より大きかったり、指定された範囲が存在しない場合はpanic
を起こします。
また、大量の要素を削除する場合は、1つずつ削除するよりもまとめて一気に削除した方が効率的だということが述べられています。
この関数により、append
やcopy
を使わずにスライスの先頭や末尾の要素の削除を直感的に書けるようになったように思います。(Delete
の中ではappend
を使っているようです)
先頭の要素を削除する場合
digits := []int64{0, 1, 2, 3, 4}
digits = slices.Delete(digits, 0, 1)
// slice: [1 2 3 4], len: 4, cap: 5
fmt.Printf("slice: %v, len: %d, cap: %d", digits, len(digits), cap(digits))
末尾の要素を削除する場合
digits := []int64{0, 1, 2, 3, 4}
digits = slices.Delete(digits, len(digits)-1, len(digits))
// slice: [0 1 2 3], len: 4, cap: 5
fmt.Printf("slice: %v, len: %d, cap: %d", digits, len(digits), cap(digits))
capacity
は渡したスライスと同じになるようです。
Insert
func Insert[S ~[]E, E any](s S, i int, v ...E) S
Insert inserts the values v... into s at index i, returning the modified slice.
The elements at s[i:] are shifted up to make room. In the returned slice r,
r[i] == v[0], and r[i+len(v)] == value originally at r[i].
Insert panics if i is out of range. This function is O(len(s) + len(v)).
指定したスライスのi
番目に要素を挿入し、変更されたスライスを返します。
挿入位置以降の要素は、挿入された要素分、右へシフトされます。
指定したインデックスが存在しない場合はpanic
を起こします。
これもDelete
と同じような理由ですが、スライスの先頭に要素を追加する際にappend
を使わずスッキリした書き方ができるようになったのではないかと思います。
digits := []int64{1, 2, 3}
digits = slices.Insert(digits, 0, 0) // 先頭に追加
fmt.Println(digits) // [0 1 2 3]
digits = slices.Insert(digits, len(digits), 4) // 末尾に追加
fmt.Println(digits) // [0 1 2 3 4]
おわりに
slices
パッケージはGo 1.21
から追加された、かなり新しいパッケージで、スライス操作をよりシンプルで効率的にする関数を提供してくれています。
今までは外部ライブラリに頼っていたりしましたが、標準ライブラリであるslices
パッケージを使って、コードの可読性とパフォーマンスを向上させましょう!
Discussion