【Go言語入門】4.配列とスライスの扱いについてさくっと学ぶ

前回まで公式をなぞっていたのですが、少し前後して今回は配列とスライスの扱いについてです。

前回の記事

配列とスライスの違いは固定長か可変長かです。

配列

package main

import (
  "fmt"
)

func main() {
  arr := [2]int{1, 2}
  fmt.Println(arr)
}

// >>> [1 2]

他の言語とほぼほぼ同じ感じですね。

ちなみに代入されていない際の要素のゼロ値は0になります。

arr := [3]int{}
// >>> [0 0 0]

スライス

package main

import (
  "fmt"
)

func main() {
  var arr []int
  fmt.Println(arr == nil)
}

// >>> true

長さを指定しなければスライスの宣言になります。

また、スライスはゼロ値がnilになります。

package main

import (
  "fmt"
)

func main() {
  var arr = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  slice := arr[2:5]
  fmt.Println(slice)
}

// >>> [3 4 5]

このような形で開始位置、終了位置のインデックス指定でスライスへと切り出すことができます。

ちなみに

package main

import (
  "fmt"
)

func main() {
  var arr = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  slice1 := arr[0:10]
  slice2 := arr[:10]
  slice3 := arr[0:]
  slice4 := arr[:]
  fmt.Println(slice1, slice2, slice3, slice4)
}

// >>> [1 2 3 4 5 6 7 8 9 10] [1 2 3 4 5 6 7 8 9 10] [1 2 3 4 5 6 7 8 9 10] [1 2 3 4 5 6 7 8 9 10]

出力結果からわかるようにこれらのスライス式は同じ値になります。

Pythonにあるようなインデックス-1は無効なようです。

map, cap, append

簡単な体操がてら組み込み関数であるスライスの長さ(length)を得るlen,、容量(capacity)を得るcap要素を追加するappendを使ってみます。

package main

import (
  "fmt"
)

func main() {
  var arr = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  arr = append(arr, 11, 12)
  fmt.Println(arr, len(arr), cap(arr))
}

// >>> [1 2 3 4 5 6 7 8 9 10 11 12] 12 20

特にlenはfor文で毎度毎度お世話になります。

make, range, _

配列・スライスを生成できるmakeスライスやマップ(所謂連想配列)の反復処理ができるrange、その際必要になるアンダースコア変数を一緒に使ってみます。

package main

import "fmt"

func main() {
  slice := make([]int, 10)
  for i := range slice {
    slice[i] = i * 2
  }
  for _, v := range slice {
    fmt.Printf("%v ", v)
  }
}

// >>> 0 2 4 6 8 10 12 14 16 18

アンダースコア変数の勘所は、Goはfor文で宣言した変数の不使用に対してエラーを吐いてしまうところです。

例えば

for i := range slice {
  slice[i] = i * 2
}
↓
for i := range slice {
  fmt.Println("hoge")
}

というように書くと、i declared and not usedのようなエラーを吐いてしまいます。

rangeを使った配列・スライスループの場合、インデックス番号と要素を割り当てた変数はブロック内での使用が必須という決まりがあります。

ただしアンダースコア変数はブロック内の変数として扱うことができません。

じゃあいつ必要なのかということですが、条件別に書いてみたほうが分かりやすかったので、簡単にまとめました。

①インデックス番号・要素どちらも使わない

for i := 0; i < len(arr); i++ {}(rangeは不要)

②インデックス番号のみ使う

for i := range arr {}

③要素のみ使う

for _, v := range arr {}

④インデックス番号・要素のどちらも使う

for i, v := range arr {}

いかがでしょうか。

要素のみを使用する予定なのに対して、④のようなループ文にしてしまうと、変数iに対してエラーが出てしまうので、代わりに_に代入させることで暗黙的に値を捨てる、という意図で使うっぽいですね。

Go言語における構文の「エラー回避変数」というのが今の所僕の認識です。

今ある知識でまとめているので、学習をもっと進めていけば更に方法論は増えたり認識違いからの修正があるかも知れません。

その時は追記していこうと思います。

Goプログラミング実践入門