Skip to content

Go slicesパッケージ Walkthrough

2023/03/09

Go

目次

目次

  1. slices パッケージについて
  2. BinarySearch
    1. BinarySearchFunc
  3. Clone
  4. Compact
  5. Compact
  6. Compare
  7. Contains
  8. Delete
  9. Equal
  10. Grow
  11. Index
  12. Insert
  13. IsSorted
  14. Replace
  15. Sort

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)

BinarySearchconstrains.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

func Clip(s S) S

スライスから埋まっていない 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

func Clone(s S) S

スライスの浅いコピーをします。そのため、要素の参照は同じです。

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

func Compact(s S) S

スライスの連続した同じ要素を 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

func Compare(s1, s2 []E) int

スライスを比較します。実装は単純な 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

func Delete(s S, i, j int) S

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

func Equal(s1, s2 []E) bool

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

func Grow(s S, n int) S

スライスのキャパシティを n 増やします。

x := make([]int, 0, 1)
y := slices.Grow(x, 3)
fmt.Println(cap(x))
fmt.Println(cap(y))
// 1
// 3

Index

func Index(s []E, v E) int

スライスに一致する要素の 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

func IsSorted(x []E) bool

スライスがソート済みか検証します。

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

func Sort(x []E)

スライスをソートします。

x := []int{3, 2, 1}
fmt.Println(x)
slices.Sort(x)
fmt.Println(x)
// [3 2 1]
// [1 2 3]

func SortFunc(x []E, less func(a, b E) bool)