go言語のslice操作をまとめてみた（shiftしたりpushしたり）


sliceの動作はそこそこ分かりやすいけれども、操作は煩雑。
自分が使ってる方法をよく忘れるのでとりあえずまとめてみました。
best practiceを求めたわけでは無いですが、一時変数など、余計なメモリ確保をしないようにはしてみました。
パフォーマンス検証とかはそのうちやります。

本家のwikiに[slice trick](https://github.com/golang/go/wiki/SliceTricks)あるのでそちらのほうも参考に。

#slice初期化

## コンポジットリテラル表記

[composite literal](https://golang.org/doc/effective_go.html#composite_literals)で直接初期値を記述しています。
この場合、capacityは初期化したときの要素数となります。

```go
// 整数のスライス
// len: 5, cap: 5
slice1 := []int{1, 2, 3, 4, 5}
// 文字列のスライス
// len: 3, cap: 3
slice2 := []string{"str1", "str2", "str3"}
```

[サンプル GoPlayground](https://play.golang.org/p/PlsJUPo0djj)

## make関数
[make function](https://golang.org/doc/effective_go.html#allocation_make)で初期化します。

スライスの長さ（length）と確保するメモリ（capacity）をそれぞれ指定できます。
当たり前ですが `length ≦ capacity` です。 
スライスのみ指定すると`capacity = length`になります。

数値は要素を0、文字列は要素を空文字列、ポインタはnilとして初期化するようです。

```
// lengthを指定
slice1 := make([]int, 5)
// slice1 = [0, 0, 0, 0, 0]

// length, capacity指定
slice2 := make([]int, 5, 10)
// slice2 = [0, 0, 0, 0, 0]
// length=10（先頭からindex10の前）でスライスすると、capacityある10要素目まで初期化されている
// slice2[:10] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

// []stringの場合
slice3 := make([]string, 5)
// slice3 = ["", "", "", "", ""]

// ポインタの場合
type myType struct {}
slice4 := make([]myType, 5)
// slice4 = [nil, nil, nil, nil, nil]
```

#slice基本操作

## インデックスで要素アクセス

lengthの範囲内でインデックスを指定して要素を参照します。
capacityの範囲内であっても、lengthの範囲外であればエラーになります。

```go
slice := []int{1, 2, 3}
fmt.Printf("%d\n", slice[0])

// 範囲外のインデックス指定はindex out of range [3]でエラーになる
// fmt.Printf("%d\n", slice[3]) 
```

## sliceの一部を切り取り（スライス）
capacityの指定などでメモリ確保された範囲内であれば、指定した範囲を参照してスライスとして利用できます。
参照なので要素を変更すると元のスライスにも反映されるので注意。

範囲の末尾は、末尾のインデックス**１つ後**を指定します。（一見わかりにくいですが、末尾＋１＝先頭＋長さになるので実際のコーディングでは扱いやすい）

```go
// 初期スライス
// インデックス  0  1  2  3  4
slice := []int{1, 2, 3, 4, 5}

// sliceから インデックス1〜3の範囲を切り取り
slice2 := slice[1:4]
// slice2 = [2 3 4]
// sliceから切り取っているのでslice2[0] と slice[1]は同じポインタ

// sliceから インデックス2〜末尾までを切り取り
slice3 := slice[2:]
// slice3 = [3, 4, 5] 

// sliceから 先頭〜インデックス2まで切り取り
slice4 := slice[:2]
// slice4 = [1, 2]
```


#slice応用操作
##sliceの末尾に要素を追加する
push動作です。ふつうにappendしているだけです。

```go:
slice = append(slice, 追加要素)
```

[サンプル GoPlayground](https://play.golang.org/p/nDuXooVBx-)

##sliceの末尾を取り出す
pop動作です。末尾を取り出してから、末尾を除いた範囲を切り出しています。
参照のみの変更なので、ポインタは変わりません。

```go:
// 要素の取り出しはポインタでなく値参照してください。
// （*をつける、copyするなど）
// 他のポインタ変数が残っていると書き換えられる可能性があるので…
末尾の要素 := slice[len(slice)-1] 
slice = slice[:len(slice)-1]　// 末尾削除
```

[サンプル GoPlayground](https://play.golang.org/p/-ClqCxOVtW)

## sliceの先頭に要素を追加する
unshift動作です。スライスの要素を右にシフトして先頭要素を置き換えています。
自身のappendなのでcapacityに余裕があればポインタは変わりません。

```go:
slice, slice[0] = append(slice[:1], slice[0:]...), 追加要素
```
[サンプル GoPlayground](https://play.golang.org/p/NSqQ4SOu05)

## sliceの先頭から要素を取り出す
shift動作です。先頭の要素を取り出してから、２番目以降の要素を切り出します。
ポインタの先頭は変わりますが、各要素のポインタは変わりません。

```go:
// 要素の取り出しはポインタでなく値参照してください。
// （*をつける、copyするなど）
// 他のポインタ変数が残っていると書き換えられる可能性があるので…
先頭の要素 := slice[0]
slice = slice[1:]
```

[サンプル GoPlayground](https://play.golang.org/p/w1Y1qGhdoD)

##任意の場所に要素を追加する
特殊な場合だとは思いますが…
先頭に追加する場合の応用です。追加する位置より後ろを右側にシフトして、空いた場所に値を入れています。
もちろんポインタは変わりません。

```go:
pos := 追加する位置
// NGコード 代入順序が保証されないからダメでした
// slice , slice[pos] = append(slice[:pos+1], slice[pos:]...), 追加する要素
slice = append(slice[:pos+1], slice[pos:]...)
slice[pos] = 追加する要素
```

[サンプル GoPlayground](https://play.golang.org/p/RvYSdmWQ8hM)

##任意の場所から取り除く

```go:
pos := 取り出す位置
slice = append(slice[:pos], slice[pos+1:]...)
```
[サンプル GoPlayground](https://play.golang.org/p/s_rQl5Fyq3_2)

# slice比較

愚直にやるなら１要素ずつ比較します。
reflect.DeepEqualですべての要素を比較する手段もよく見かけますが、内部構造がある場合は注意してください。
[]byteはbytes.Equalsも使えます。

```go:

slice1 := []int{1, 2, 3}
slice2 := []int{1, 2, 3}

cmp := true
for i, e := range slice1 {
  if e!= slice2[i] {
    cmp := false
    break 
  }
}

// cmp == true

// 要素がintなどの型はこれでも可。structなどであれば予期せぬ比較がされる可能性があるので要注意。
cmp := reflect.DeepEqual(slice1, slice2) // true

// byte はbytes.Equalsが使えます。
bytes.Equal([]byte{1,2,3,4}, []byte{1,2,3,4}) // true
```
[サンプル GoPlayground](https://play.golang.org/p/VOvr7u86eGH)

reflect.DeepEqual
[サンプル GoPlayground](https://play.golang.org/p/GqDI0-Q7cK6)

よりスマートな方法ご存じであればぜひ教えて下さいm(_ _)m

ちなみに２次元スライスはこちら
https://qiita.com/egnr-in-6matroom/items/859559296f8e78bffd34


さらに詳しい動きは[ソース](https://github.com/golang/go/blob/master/src/runtime/slice.go)を読むこと。すべてgoのコードで語ってくれるのがgoの良いところ…