ゆるりのこと。

文系営業マンから新米データサイエンティストしています。

A Tour of Goのざっくりまとめ④ <More types: 配列(Arrays), Slice, make編>

前回までの続きです。
mattsun-plapla.hatenablog.com
mattsun-plapla.hatenablog.com
mattsun-plapla.hatenablog.com


配列

[n]T 型は、型 T の n 個の変数の配列( array )を表します。

func main() {
	var a [2]int
	a[0] = 300
	a[1] = 500
	fmt.Println(a) // [300 500]
	
        // 定めたインデックス数以上の数を配列に配置できない
        a[2] = 1000 // invalid array index 2 (out of bounds for 2-element array)
}

/* 最初に値を入れる場合 */
func main() {
	var b [2]int = [2]int{500, 1000}
        // or
	c := [3]string{"a", "b", "c"}
	fmt.Println(b) // [500, 1000]
	fmt.Println(c) // [a, b, c]
}
  • 同じデータ型を格納する
  • デフォルト値はデータ型に応じて変わる(int=0, string="", etc..)
  • 配列は最初に定めたサイズを変えることができない。つまり固定長である。
  • 可変長にしたい場合、後述のSliceを使う。

Automatic array length declaration

[n]Tにおけるnを定めなくとも、配列数を推論してくれるもの。
この場合、[...]Tと書く。

func main(){
	i := [...]int{
		1,2,3,4,
	}
	s := [...]string{
		"japan",
		"korea",
		"thailand",
		"usa",
	}
	fmt.Println(i) // [1 2 3 4]
	fmt.Println(s) // [japan korea thailand usa]
}

配列同士の比較

配列同士の比較では、データ型、配列数、要素の順序が合致していないと同じとみなされない。

func main(){
	a := [3]int{1,2,3}
	b := [3]string{"1", "2", "3"}
	c := [3]int{2,1,3}
	fmt.Println(a==b) // (mismatched types [3]int and [3]string)
	fmt.Println(a==c) // false
}

Multi-demantional array

後述のスライスにも書いていますが、配列内配列も下記のように作れる。
ただし、配列内はすべて同じデータ型である必要がある。

[n][m]T型は、m個の要素をもつ配列がn個あることを表します。

func main(){
	a := [2][3]int{
		[3]int{1,2,3},
		[3]int{4,5,6},
	}
	fmt.Println(a) // [[1 2 3] [4 5 6]]

        /* short syntaxとしてこのようにも書ける */
        a := [3][2]int{{1,2},{3,4},{5,6}}
	fmt.Println(a) // [[1 2] [3 4] [5 6]]
}

多次元配列の要素を1つずつFor loopで取り出す場合の一例

func main(){
	a := [3][2]int{{1,2},{3,4},{5,6}}

	for _, outsideArray := range a{
		for _, insideArray := range outsideArray{
			fmt.Println(insideArray) // 1,2,3,4,5,6
		}
	}
}

Slice

型 []T は 型 T のスライスを表します。

  • スライスは配列への参照のようなもの
  • 同じデータ型である必要はあるものの、配列内要素数の制約がない。(先述のように、異なるデータ型でもいいのはstruct)
  • 可変長配列であるスライスでは、[]の中に配列数を指定しない
  • 後から配列に要素を追加することもできる。また、配列内配列をつくることもできる
  • インデックス指定で値を取り出す場合、最後の要素は含まれない (ex: 1:4なら1~3まで)
  • スライスの要素を変更すると元の対応する配列要素も変更される
  • スライスのゼロ値はnil (配列との違い。配列は定めた要素数分の初期値を返す)

また、おそらく、Pythonのように、[-1]といった取り出し方はできない模様。

スライスが配列への参照であること

スライスは配列を参照しているので、スライス自体がデータを持つのではなく、配列にあるデータをもっている。
var s []intのような、配列内にデータがない場合は、スライスは配列を参照していないため、nilが返される。
また、スライス内のある要素を書き換えた場合、その更新は元の配列へも反映される。

func main() {
	sl := []int{1, 2, 3, 4, 5, 6}
	fmt.Println(sl)      // [1 2 3 4 5 6]
	fmt.Println(sl[1])   // 2
	fmt.Println(sl[1:3]) // [2 3]
	fmt.Println(sl[:3])  // [1 2 3]
	fmt.Println(sl[3:])  // [4 5 6]

        /* 値を変更 */
	sl[2] = 1000
	fmt.Println(sl) // [1 2 1000 4 5 6]

        /* 値を追加 */
	sl = append(sl, 5000)
	fmt.Println(sl) // [1 2 1000 4 5 6 5000]

        /* 配列に配列を追加 */
	var double = [][]int{
		[]int{1, 2, 3},
		[]int{4, 5, 6},
		[]int{7, 8, 9},
	}
	fmt.Println(double) // [[1 2 3] [4 5 6] [7 8 9]]
}

スライス同士の比較

スライスはnilとしか比較できない。要素同士を比較したい場合、ループを回して要素を取り出すか、他のパッケージを利用するしかない。

make

built-inのmake関数をつかってスライスをつくることもできる。

The make function allocates a zeroed array and returns a slice that refers to that array
https://tour.golang.org/moretypes/13

ということで、配列と配列の容量を決めることができる。
スライスが返されるため、生成時に定めた配列を拡大することができる。
一方で、容量以上の配列を作成しようとするとエラーになる。(下記例の最後の文)

func main() {
	/* 2番目が配列数、3番目が配列の容量(capacity) */
	s1 := make([]int, 3)
	fmt.Printf("len=%d cap=%d val=%v\n", len(s1), cap(s1), s1) // len=3 cap=3 val=[0 0 0]

	s2 := make([]int, 3, 5)
	fmt.Printf("len=%d cap=%d val=%v\n", len(s2), cap(s2), s2) // len=3 cap=5 val=[0 0 0]

	s3 = append(s3, 0, 0)
	fmt.Printf("len=%d cap=%d val=%v\n", len(s3), cap(s3), s3) // len=5 cap=5 val=[0 0 0 0 0]

	s4 = append(s4, 0, 0, 1, 2)                                // Sliceなのでなので定めた容量以上を追加することもOK
	fmt.Printf("len=%d cap=%d val=%v\n", len(s4), cap(s4), s4) // len=9 cap=10 val=[0 0 0 0 0 0 0 1 2]
	// s4 := make([]int, 5, 3) // error: len larger than cap in make([]int)

参考

/* */