前回までのあらすじ
第3回目は「型アサーション」について学びました。もしよかったらご覧ください。
今回も引き続きUdemyの『【Go入門】Golang基礎入門 + 各種ライブラリ + 簡単なTodoWebアプリケーション開発(Go言語)』の内容をもとに学習を進めていきます。
スライスとは
配列とはちょっと違ったものがスライスです。配列よりも使い勝手が良いので、Go言語ではよくみられるものになっています。これから細かくみていきます。配列と違って[]
内に数値を入れなければスライスになります。
//明示的
var sl []int = []int{100, 200}
// [100 200]
//暗黙的
sl := []int{100, 200}
// [100 200]
//make関数
sl := make([]int, 5)
// [0 0 0 0 0]
値の更新と取り出し
値の更新と取り出しはインデックス番号を指定してあげることでできます。取り出す際は:
を使うことである特定の間の要素を取り出すことも可能です。
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
関数が使われます。
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
はメモリを気にする必要がある開発(パフォーマンスを重視するもの)の際に利用するものなので初学では意識しすぎるのは良くないかもしれないです。
sl := []int{1, 2, 3}
fmt.Println(len(sl))
// 3
fmt.Println(cap(sl))
// 3
私の脳のキャパシティーも限界になりそうなので、深追いはせず、いつか必要になったときに学習したいと思います。
copy
関数
スライスは参照型であるため、新しい変数に値を代入するだけだとコピーされず、コピー元が更新されるとコピー先も更新されてしまいます。具体的にはこんな感じです。下の例ではslcopy
には[1 2 3](値更新前)を期待しているのに、更新後の[100 2 3]と同じようになってしまいます。
sl := []int{1, 2, 3}
slcopy := sl
sl[0] = 100
fmt.Println(sl)
// [100 2 3]
fmt.Println(slcopy)
// [100 2 3]
ここで使われるのがcopy
関数です。copy関数を用いることで、値が参照されることなく、新しいスライスとして変数に値が残り続けます。
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
スライスの中を一つずつ取り出しための関数です。スライスの中に複数個の要素が入っている場合に便利です。
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
などで1つずつ取り出すことが可能になっています。
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関数についてみていきます。
//インデックスを文字列にして、値を整数にした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]
値の取り出し
正直仕組みはスライスとほぼ変わりません。なので説明もなく、さらっとコードで示します。
//インデックスを文字列にして、値を整数にした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型の場合は
を返します。これだと予期せぬ入力をしてしまう可能性があります。それに対応するために配列のエラーハンドリングを使います。
m := map[string]int{
"one": 1,
"two": 2,
"three": 3,
}
fmt.Println(m["ten"]) // 配列に含まれていないインデックス
// 0 (予期せぬエラーになる可能性)
エラーハンドリングの導入
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
map関数には他にもdelete関数やlen関数、rangeなどが使えます。
ちょっと力尽きてしまったので、今回は説明しません。
これからmap関数を使ってブイブイ言わせる人はぜひ調べてみてください。