目次
目次
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"))
// hogesort パッケージ の 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 3BinarySearchFunc
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: 1Clone
スライスの浅いコピーをします。そのため、要素の参照は同じです。
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
// 1func 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)
// truefunc 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
// falsefunc 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
// 3Index
スライスに一致する要素の index を返します。
x := []int{1, 2, 3, 4, 5}
target := 4
fmt.Println(slices.Index(x, target))
fmt.Println(slices.Index(x, 9999))
// 3
// -1func 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
// falsefunc 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]