目次
目次
slices パッケージについて
slices パッケージ は スライスにおいてジェネリックなプログラミングをできるようにするための便利関数が提供されています。
互換性を保つため、試験的な exp package として提供されています。
実装は sort パッケージ とほぼ同じなため、サンプルコードを載せつつジェネリクスの部分だけ実装を追います。
具体的な実装やユースケースは sort パッケージ Walkthroughをご覧ください。
$ gocloc --not-match=test slices
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Go 4 133 277 934
-------------------------------------------------------------------------------
TOTAL 4 133 277 934
-------------------------------------------------------------------------------
BinarySearch
func BinarySearch(x []E, target E) (int, bool)
BinarySearch
は constrains.Ordered
とそのスライスを引数で受け取り、スライスのターゲットの位置と見つかったかを返します。
関数名の通り、バイナリサーチを利用するためスライスはソート済みである必要があります。
type Ordered interface は以下の制約になっています。
type Ordered interface {
Integer | Float | ~string
}
~
(チルダ)をつけることで、その方の基礎となる型のUnderlyingType となり、基礎の型が string
であるものを受け入れることが出来ます。
type MyString string
func Log[T ~string](t T) {
fmt.Printf("%d", t)
}
Log(MyString("hoge"))
// hoge
sort パッケージ の func Search(n int, f func(int) bool) int と似ています。
any 型からconstrains.Ordered
のスライスの制約になっていることで、スライスの長さ n
が不要になっています。
x := []int{1, 2, 3, 4, 5}
target := 4
i, found := slices.BinarySearch(x, target)
if found {
fmt.Printf("found %d at entry %d\n", target, i)
} else {
fmt.Printf("%d not found, would insert at %d", target, i)
}
// found 4 at entry 3
BinarySearchFunc
func BinarySearchFunc(x []E, target T, cmp func(E, T) int) (int, bool)
func BinarySearch(x []E, target E) (int, bool)の比較部分を指定できます。
xs := []int{1, 2, 3, 4, 5}
target := 4
i, found := slices.BinarySearchFunc(xs, target, func(x, target int) int {
if x < target {
return -1
} else if x == target {
return 0
} else {
return 1
}
})
if found {
fmt.Printf("found %d at entry %d\n", target, i)
} else {
fmt.Printf("%d not found, would insert at %d", target, i)
}
// found 4 at entry 3
スライスから埋まっていない capacity を取り除きます。
func Clip[S ~[]E, E any](s S) S {
return s[:len(s):len(s)]
}
x := make([]int, 0, 10)
fmt.Printf("cap: %d, len: %d\n", cap(x), len(x))
x = append(x, 1)
fmt.Printf("cap: %d, len: %d\n", cap(x), len(x))
x = slices.Clip(x)
fmt.Printf("cap: %d, len: %d\n", cap(x), len(x))
// cap: 10, len: 0
// cap: 10, len: 1
// cap: 1, len: 1
Clone
スライスの浅いコピーをします。そのため、要素の参照は同じです。
x := []int{1, 2, 3, 4, 5}
y := slices.Clone(x)
x = append(x, 6)
fmt.Println(x)
fmt.Println(y)
// [1 2 3 4 5 6]
// [1 2 3 4 5]
Compact
スライスの連続した同じ要素を 1 つのみ残し削除した新しいスライスを返します。
x := []int{1, 2, 1, 2, 2, 2}
y := slices.Compact(x)
fmt.Println(x)
fmt.Println(y)
// [1 2 1 2 2 2]
// [1 2 1 2]
func CompactFunc(s S, eq func(E, E) bool) S
Compact
func Compact(s S) S と同じですが、比較部分を引数に指定できます。
type comparable interface{ comparable }
ではない構造体において有効です。
x := []struct {
Name string
Age int
}{
{"Gopher", 7},
{"Alice", 55},
{"Bob", 75},
{"Bob", 75},
}
y := slices.CompactFunc(x, func(x, target struct {
Name string
Age int
}) bool {
return x.Name == target.Name && x.Age == target.Age
})
fmt.Println(x)
fmt.Println(y)
// [{Gopher 7} {Alice 55} {Bob 75} {Bob 75}]
// [{Gopher 7} {Alice 55} {Bob 75}]
Compare
スライスを比較します。実装は単純な for ループです。
func Compare[E constraints.Ordered](s1, s2 []E) int {
s2len := len(s2)
for i, v1 := range s1 {
if i >= s2len {
return +1
}
v2 := s2[i]
switch {
case v1 < v2:
return -1
case v1 > v2:
return +1
}
}
if len(s1) < s2len {
return -1
}
return 0
}
x := []int{1, 2, 3}
y := []int{1, 2, 3}
z := []int{0, 0, 0}
fmt.Println(slices.Compare(x, y))
fmt.Println(slices.Compare(x, z))
// 0
// 1
func CompareFunc(s1 []E1, s2 []E2, cmp func(E1, E2) int) int
Contains
func Contains(s []E, v E) bool
スライスの中に要素 v
が含まれるか検証します。
実装は Index 関数を呼んでいるだけです。
func Contains[E comparable](s []E, v E) bool {
return Index(s, v) >= 0
}
x := []int{1, 2, 3, 4, 5}
target := 4
r := slices.Contains(x, target)
fmt.Println(r)
// true
func ContainsFunc(s []E, f func(E) bool) bool
Delete
index i
から j
の範囲の要素を削除します。引数へ渡すスライス s
に影響します。
x := []int{1, 2, 3, 4, 5}
y := slices.Delete(x, 0, 1)
fmt.Println(x)
fmt.Println(y)
// [2 3 4 5 5]
// [2 3 4 5]
Equal
2つのスライスが等しいか比較します。実装は、長さを検証し for ループを回します。
comparable のスライスである制約があることでコンパイルエラーを見つけることができます。
func Equal[E comparable](s1, s2 []E) bool {
if len(s1) != len(s2) {
return false
}
for i := range s1 {
if s1[i] != s2[i] {
return false
}
}
return true
}
x := []int{1, 2, 3}
y := []int{1, 2, 3}
z := []int{0, 0, 0}
fmt.Println(slices.Equal(x, y))
fmt.Println(slices.Equal(x, z))
// true
// false
func EqualFunc(s1 []E1, s2 []E2, eq func(E1, E2) bool) bool
Grow
スライスのキャパシティを n
増やします。
x := make([]int, 0, 1)
y := slices.Grow(x, 3)
fmt.Println(cap(x))
fmt.Println(cap(y))
// 1
// 3
Index
スライスに一致する要素の index を返します。
x := []int{1, 2, 3, 4, 5}
target := 4
fmt.Println(slices.Index(x, target))
fmt.Println(slices.Index(x, 9999))
// 3
// -1
func IndexFunc(s []E, f func(E) bool) int
Insert
func Insert(s S, i int, v …E) S
スライスに、指定した index i
へ要素を追加します。
x := []int{1, 2, 3}
y := slices.Insert(x, 3, 4, 5)
fmt.Println(x)
fmt.Println(y)
// [1 2 3]
// [1 2 3 4 5]
IsSorted
スライスがソート済みか検証します。
x := []int{1, 2, 3}
y := []int{3, 2, 1}
fmt.Println(slices.IsSorted(x))
fmt.Println(slices.IsSorted(y))
// true
// false
func IsSortedFunc(x []E, less func(a, b E) bool) bool
Replace
func Replace(s S, i, j int, v …E) S
スライスの index i
j
の範囲を v
で置換します。
func TestSlicesReplace(t *testing.T) {
x := []int{1, 2, 3}
y := slices.Replace(x, 0, 1, 9, 9)
fmt.Println(x)
fmt.Println(y)
}
// [1 2 3]
// [9 9 2 3]
Sort
スライスをソートします。
x := []int{3, 2, 1}
fmt.Println(x)
slices.Sort(x)
fmt.Println(x)
// [3 2 1]
// [1 2 3]