【Go言語】　スライス

# 前回までのあらすじ
第3回目は「型アサーション」について学びました。もしよかったらご覧ください。

https://qiita.com/koutorino/items/b1e335bc30618f5470be


今回も引き続きUdemyの『【Go入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』の内容をもとに学習を進めていきます。

https://www.udemy.com/course/golang-webgosql/?couponCode=ST11MT91624B

# スライスとは
配列とはちょっと違ったものがスライスです。配列よりも使い勝手が良いので、Go言語ではよくみられるものになっています。これから細かくみていきます。配列と違って`[]`内に数値を入れなければスライスになります。

```golang
//明示的
var sl []int = []int{100, 200}
// [100 200]

//暗黙的
sl := []int{100, 200}
// [100 200]

//make関数
sl := make([]int, 5)
// [0 0 0 0 0]
```

## 値の更新と取り出し
値の更新と取り出しはインデックス番号を指定してあげることでできます。取り出す際は`:`を使うことである特定の間の要素を取り出すことも可能です。
```golang
sl := []int{1, 2, 3, 4, 5}

// 値の更新
sl[1] = 10
// [1 10 3 4 5]

// 値の取り出し
fmt.Println(sl) // [1 10 3 4 5]
fmt.Println(sl[1]) // 10
fmt.Println(sl[1:4]) // [10 3 4]
fmt.Println(sl[:2]) // [1 10]
fmt.Println(sl[2:]) // [3 4 5]
fmt.Println(sl[:]) // [1 10 3 4 5]
```

## `append`関数
スライスが配列と比べて良い点が可変長であり、サイズを自由に変更できるところにあります。この特徴を活かしたお供として、`append`関数が使われます。
```golang
sl := []int{1, 2, 3}

sl = append(sl, 4)
// [1 2 3 4]
sl = append(sl, 5, 6, 7)
// [1 2 3 4 5 6 7]
```

## `len`関数と`cap`関数
要素数の数を数えるための関数として`len`関数が用意されています。また、キャパシティーを出力する`cap`関数というものがあります。ただ、`cap`はメモリを気にする必要がある開発(パフォーマンスを重視するもの)の際に利用するものなので初学では意識しすぎるのは良くないかもしれないです。
```golang
sl := []int{1, 2, 3}

fmt.Println(len(sl))
// 3
fmt.Println(cap(sl))
// 3
```
私の脳のキャパシティーも限界になりそうなので、深追いはせず、いつか必要になったときに学習したいと思います。

## `copy`関数
スライスは参照型であるため、新しい変数に値を代入するだけだとコピーされず、コピー元が更新されるとコピー先も更新されてしまいます。具体的にはこんな感じです。下の例では`slcopy`には[1 2 3]（値更新前）を期待しているのに、更新後の[100 2 3]と同じようになってしまいます。
```golang
sl := []int{1, 2, 3}
slcopy := sl

sl[0] = 100
fmt.Println(sl)
// [100 2 3]
fmt.Println(slcopy)
// [100 2 3]
```

ここで使われるのが`copy`関数です。copy関数を用いることで、値が参照されることなく、新しいスライスとして変数に値が残り続けます。
```golang
sl := []int{1, 2, 3}
slcopy := make([]int, 3)
copy(slcopy, sl)

sl[0] = 100
fmt.Println(sl)
// [100 2 3]
fmt.Println(slcopy)
// [1 2 3]
```
## スライスのfor
スライスの中を一つずつ取り出しための関数です。スライスの中に複数個の要素が入っている場合に便利です。
```golang
sl := []int{1, 2, 3, 4, 5}

for i, v := range sl {
	fmt.Println(i, v)
}
// インデックス番号 要素
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5

for _, v := range sl {
	fmt.Println(i, v)
}
// 要素(_を用いることで値のみも取り出せる)
// 1
// 2
// 3
// 4
// 5

// これでも同じような振る舞いは可能
for i := 0; i < len(sl); i++ {
	fmt.Println(i, sl[i])
}

```

## 可変長引数
関数を作成し、その関数の引数にいくつかの値を渡すときに`(...<型>)`とすることでスライスを関数に渡し、`range`などで１つずつ取り出すことが可能になっています。
```golang
func Sum(s ...int)int {
	sum := 0
	for _, v := range s {
		sum += v
	}
	return sum
}

func main() {
// 直接数値を渡す場合
fmt.Println(Sum(1, 2, 3, 4, 5))
// 15

//スライス経由で渡す場合
sl := []int{1, 2, 3, 4, 5}
fmt.Println(Sum(sl...))
// 15
}
```

# 外伝　map
スライスではないのですが、インデックスに値を持たせることができる配列を作成するmap関数についてみていきます。
```golang
//インデックスを文字列にして、値を整数にしたmap
m := map[string]int{
    "one": 1,
    "two": 2,
    "three": 3,
}
fmt.Println(m)
 // map[one:1 three:3 two:2] 

//インデックスを整数にして、値を文字列にしたmap
mr := map[int]string{
    1: "one",
    2: "two",
    3: "three",
}
fmt.Println(mr)
 // map[1:one 2:two 3:three]
```

## 値の取り出し
正直仕組みはスライスとほぼ変わりません。なので説明もなく、さらっとコードで示します。
```golang
//インデックスを文字列にして、値を整数にしたmap
m := map[string]int{
    "one": 1,
    "two": 2,
    "three": 3,
}
fmt.Println(m["one"])
 // 1 
fmt.Println(m["two"])
 // 2
```

1つ落とし穴があります。それは配列のインデックスにない要素の取り出しを行なった場合でも、Goでは初期値を返すようになっています。つまり、配列にない要素についてはint型の場合は`0`、string型の場合は` `を返します。これだと予期せぬ入力をしてしまう可能性があります。それに対応するために配列のエラーハンドリングを使います。
```golang
m := map[string]int{
    "one": 1,
    "two": 2,
    "three": 3,
}

fmt.Println(m["ten"]) // 配列に含まれていないインデックス
//　0 (予期せぬエラーになる可能性)
```

エラーハンドリングの導入
```golang
m := map[string]int{
    "one": 1,
    "two": 2,
    "three": 3,
}
s, ok := m["ten"]
if !ok {
    fmt.Println("key is not found")
}
fmt.Println(s)
// key is not found
```

:::note info
map関数には他にもdelete関数やlen関数、rangeなどが使えます。
ちょっと力尽きてしまったので、今回は説明しません。
これからmap関数を使ってブイブイ言わせる人はぜひ調べてみてください。
:::

# まとめ
### スライスはよく見るし、使うやつ