Goの学習を行った記録としてこの記事に残す。
この記事では Goでの型についてまとめていく。
基本型
Goの基本型は他の言語同様以下のものが挙げられる。
- 論理型
- 整数型
- 文字列型
- 浮動小数点数型
それぞれの型について詳しく見ていく。
論理型
真偽値の値を持つ変数。tureまたはfalse。boolのゼロ値はfalse
整数型
整数型のゼロ値は0
intとint32やint64では、代入・比較・演算は型変換をしない限りコンパイルエラーとなる。
整数リテラルはデフォルトでint型となる
演算について、整数の割り算の結果は整数となる。浮動小数点の結果がほしければ浮動小数点型に変換が必要。
文字列型
文字列のゼロ値は””空文字列
runeリテラルのデフォルト型はrune
文字列リテラルの基本型はstring
文字列を参照しているときはint32ではなくruneを使用する
浮動小数点数型
既存フォーマットとの互換性を気にしなくて良いのであれば、float64だけ使えば良い。
そもそも浮動小数点数を使うかの検討が必要で、ほとんどの場合は不要。
浮動小数点数を比較するのに==や≠は使えるけど避けた方が良い。
等しいかどうかではなく差が一定の範囲内であるかを判定するのが正しい。
合成型
基本型を使って作られる合成型(複合データ型)は以下のものが挙げられる。
- 配列
- スライス
- マップ
- 構造体
それぞれの型について詳しく見ていく。
配列
Goにも配列(array)はあるが、使われることは多くない。
配列の宣言の仕方はいくつかある
①配列の大きさと要素の型を指定する方法
var x [3]int
この宣言で三つの整数を持つ配列ができる、値が指定されていないのでintのゼロ値の0が入っている。
②値を指定する方法
var x = [3]int{10,20,30}
上記のように、「配列リテラル」を使って定義する
配列に対し、要素を超えるインデックスを指定したり、インデックスに負の値を指定したりはできない。このような指定をした場合、コンパイルエラーとなる。
インデックスを変数で指定した場合はコンパイルされるが、値が範囲外になると実行に失敗し「パニック」になる。
Goで配列がなぜあまり使われないかというと、「制限が多い」から。
Goでは配列の大きさを配列の「型」の一部として扱っているので[3]intと[4]intは型が異なると判断される。
配列を使うときは、事前に配列のサイズが輪っかている時のみ。
スライス
一連の値を保持するためのデータ構造なら、ほとんどが可変長の配列のスライスを使うのが正解。
スライスの型には長さが入らないので配列のような制限はない。
スライスと配列は似ているが、少し違う。
一つ目に長さの指定をしない。
[n]または[…]と書くと配列、[]だけを書くとスライスになる。
また下記のようなコードは整数のスライスを生成する。値は一つも代入されていないのでxにはスライスのゼロ値が代入される。スライスのゼロ値はnilになり、Goにおいてはnilはいくつかの型において「値がない」状態を示す。
var x []int
スライス同士の比較はできず、==や≠で判定しようとするとコンパイルエラーとなる。
スライスと比較できるのはnilかどうかだけ。
-
len
組み込み関数lenで配列の長さを取れる。スライスにも使える。
nilスライスをlenに渡すと0が返ってくる。 -
append
スライスの要素を増やすにはappendを使用する。
var x []int
x = append(x, 100)
appendは少なくとも2個の引数が必要で(任意の型のスライス, 同じ型の値)、
下記のように複数の値の代入も可能。
x = append(x, 200, 300, 400)
-
キャパシティ
スライスは自動的にキャパシティ(容量)を確保指定してくれる。
appendして要素を追加しても自動的に容量を増やしてくれる。
もし、あらかじめ最大のサイズがわかっているときはキャパシティを指定した方が効率的になる。
それを実装するにはmakeを使用する。 -
make
スライスでは事前にキャパシティを指定できないが、makeでは型や長さ、オプションでキャパシティを指定できる。
x := make([]int, 5)
上記の場合は長さ5、キャパシティ5のスライスが生成される。
マップ
スライスは順番に並んでいるデータを扱う時に便利なデータ構造ですが、マップ(map)を使うとデータ間のランダムな関連が表現できる。
このマップは言語によっては辞書・連想配列・ハッシュと呼ばれる。
mapTest := map[stirng]int{}
この場合、空のマップリテラルを使っていることになるがこれはnilマップを指定するのとは違った意味になる。このマップのサイズも0だが、空のマップリテラルで初期化をすると読み書きの両方が可能になる。
マップリテラルの本体は「キー:値」の形式で書く。(最後のデータにも「,」は必要)
マップのサイズがある程度想定できる時はサイズを指定してmakeを呼び出す。
makeMap := make(map[string]int, 10)
makeを使って生成されたマップの要素数は0。最初に指定したサイズ以上のサイズになることもできる。
以下マップの特徴
- 新たなキーと値のペアを指定することで大きくできる
- 関数lenを使ってキーと値の数がわかる
- あらかじめ要素の個数がわかっている場合はmakeを使ってサイズを指定して生成できる
- ゼロ値はnil
- マップの比較はできない。nilと等しいかは比較できる
マップとスライスの用途の違いとして、スライスは順番で処理されているリストなどに向いていて、順番に並んでいないような値をキーとして用いるのときはマップを使うのが適している。
-
マップの読み書き
マップmに対してキーkに対応する値はm[k]で取得できる。また、m[k] = v でkの値をvにできる。
:=はここでは使えないことに注意。
値乗せってされていないキーを取り出そうとするとそのマップの値の型のゼロ値が返される。 -
カンマokイディオム
指定のキーがマップにあるかどうかを知りたいときはカンマokイディオムを使って「ゼロ値と結び付けられているキー」か「マップに存在しないキー」を区別する。
m := map[string]int{
"age": 10
}
v, ok := m["age"]
fmt.Println(v, ok) //10 true
v, ok := m["name"]
fmt.Println(v, ok) //0 false
構造体(struct)
マップには限界がある、マップはキーと値の型が全て同じでなければならないという点。
関数間でデータのやり取りをするには理想的とは言えない中で、構造体(struct)を使うのが良い。
type person struct {
"name" string
"age" int
"height" int
}
構造体を分けるのに「,」はいらない。
構造体の定義は関数の内でも外でも可能。
関数内で定義した場合はその関数内のみ。
構造体を宣言したらその構造体を持つ変数を定義できる。
var mike person
この場合値が代入されていないので、構造体personのゼロ値で初期化される。(各フィールドのゼロ値)
構造体リテラルを変数に代入することが可能
sam := person{} //構造体リテラル。全フィールドがゼロ値で初期化
//値を指定する場合
cory := person{
name: "コリー",
age: 25,
height: 180,
}
fmt.Println(cory.name) //コリー
値を指定しなかったフィールドはその方のゼロ値になる。
構造体のフィールドにアクセスするには「.」を使う。