Go言語 基礎講座 #5 データ型
この記事は辞書のように使っていただければと思っています。
目次
データ型の分類
Goのデータ型は4種類に分類する事ができます。それは
- 基本型
- 合成型
- 参照型
- インターフェース型
の4つです。
基本型
基本型に属するのは整数型・浮動小数点数型・複素型・ブーリアン型・文字列型です。
整数型
符号付き整数
- int (32bit または 64bit)
- int8 : -128 ~ 127
- int16 : -32768 ~ 32767
- int32 : -2147483648 ~ 2147483647
- int 64 : -9223372036854775808 ~ 9223372036854775807
- rune : -2147483648 ~ 2147483647。int32のエイリアスで慣習的にUnicodeのコードポイントである事を示すために使用。
符号なし整数
- uint (32bit または 64bit)
- uint8 : 0 ~ 255
- uint16 : 0 ~ 65535
- uint32 : 0 ~ 4294967295
- uint 64 : 0 ~ 18446744073709551615
- byte : 0 ~ 255。unit8のエイリアスで、慣習的に数値・量ではなく生データである事を示すために使用。
- uintptr : ポインタ値。GoプログラムがOSなどとやり取りする際の低レベルプログラミングなどのみで使用。
浮動小数点数型
- float32
- float64
複素型
複素数を表現する型です。
ブーリアン型
TrueかFalseのみの真偽値型です。
文字列型
文字列は不変なバイト列と定義されます。
- string
文字列には2つの種類があります。
文字列の長さを取得する
str1 := "ABCDE" str2 := "あいうえお" fmt.Println("str1のバイト数=", len(str1)) // str1のバイト数=5 fmt.Println("str2のバイト数=", len(str2)) // str2のバイト数=10
len()関数は文字列のバイト長を取得するので、上の例のように英数字の場合は文字数=バイト長になりますが、平仮名などの場合は文字数*2=バイト長になります。
純粋に文字数を取得したい場合は以下を使います。
import ( "utf8" ) str1 := "ABCDE" str2 := "あいうえお" fmt.Println("str1の文字数=", utf8.RuneCountInString(str1)) // str1の文字数=5 fmt.Println("str2の文字数=", utf8.RuneCountInString(str2)) // str2の文字数=5
utf8というパッケージのRuneCountInString()関数を使う事で純粋な文字数を取得する事ができます。
文字列を扱うための標準パッケージ
bytes
バイト型のスライス([]byte
)を扱うための関数を提供しています。
- Buffer型 :
bytes.Buffer
。bytesがバイトスライスを効率よく扱うために用意されているデータ型。
strconv
string convertの略で、整数型やブーリアン型などを文字列型に変換・逆変換する事ができます。
import ( "strconv" ) x, _ := strconv.ParseInt("12345", 10, 32) fmt.Println("x =", x) // x = 12345
ParseInt関数を使うと文字列→整数の変換ができます。第一引数が文字列で第二引数が基数(何進数か)、第三引数がビット数です。上の例の場合、32ビット10進数に変換しています。"ABC"のような数値に変換できない文字列を入力するともちろんエラーになります。
ちなみに戻り値の _ はGoでは使用しない戻り値などを明示的に示すための方法です。
x, err := strconv.Atoi("12345") if err != nil { fmt.Println("変換エラー") } fmt.Println("x =", x) // x = 12345
Atoi()関数は文字列→整数の別の変換方法です。これは文字列だけを入力すればよいのでシンプルです。
import ( "reflect" ) x := int64(12345) y := strconv.FormatInt(x, 10) fmt.Printf("y = %s, type = %s", y, reflect.TypeOf(y)) // y = 12345, type = string
整数→文字列の変換をするにはFormatInt()関数を使います。第一引数は数値で、第二引数は基数です。
fmt.Println("y =", strconv.Itoa(12345)) // y = 12345
Itoa()関数は整数→文字列の別の変換方法で非常にシンプルです。数値を入力するだけで文字列を返してくれます。
unicode
runeを分類や変換するための関数を提供してます。この文字は大文字なのかなどを判別できたりします。
合成型
合成型には配列や構造体があります。
配列
空配列
空(0)配列は以下のように定義します。
var array1[10] byte // 整数(int8)配列 var array2[3][5] int //多次元整数配列 fmt.Println("array1 =", array1) // array1 = [0 0 0 0 0 0 0 0 0 0] fmt.Println("array2 =", array2) // array2 = [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]
var 配列名[配列の長さ] データ型 で定義すると、そのデータ型のその長さの空配列が生成されます。
参照・代入
var array[3] int array[0] = 1 array[1] = 2 array[2] = 3 for _, value := range array{ fmt.Println("value =", value) } /* value = 1 value = 2 value = 3 */
配列のあるインデックスの値を参照するには上の例のように行います。
初期化
配列の定義の時点で予め値で初期化する方法です。
array := [...]string {"あ", "い", "う", "え", "お"} for _, value := range array{ fmt.Println(value) } /* あ い う え お */
配列名 := [...]型 {初期値} と書きます。...と書くことで初期値の数に合わせて自動的に配列の長さを調節してくれます。
もう一つ初期化する方法があります。
var array [5]string = [5]string {"あ", "い", "う", "え", "お"} for _, value := range array{ fmt.Println(value) } /* あ い う え お */
これは配列の変数を定義し、そこに初期化した無名の配列を代入する形になっていあます。ここで注意しなければいけないのは、定義した配列の長さと、初期値が入った無名の配列の長さは一致させておく事です。
構造体
構造体を作成
Goにはオブジェクト指向言語のクラスなるものはありません。その代わりにデータやメソッドなどをひとまとめにする構造体というデータ型が用意されています。
var strct struct{ x string y int z bool } strct.x = "ABCDE" strct.y = 10 strct.z = true fmt.Printf("x =%d, y =%s, z =%t", strct.x, strct.y, strct.z) // x = ABCDE, y = 10, z = true
構造体はこのように作成します。struct{}で構造体を作成でき、それを変数strctに代入しています。中のxyzはフィールドと呼ばれデータを格納できます。フィールド名 型 のようにして定義します。
また構造体のフィールドの参照は、 構造体.フィールド名 で行います。
構造体の作成時に任意のフィールド値で初期化することも可能です。
var customer struct{ Name string Age uint8 Email string }{ Name: "田中浩" Age: 35 Email : "hiroshi111@gmail.com" }
このように構造体の後ろに{フィールド名: 値}という記法を追加することで初期化する事ができます。
type / 構造体を定義
type Student strunct{ Name string Year uint8 Club string } func StructureType() { var student1 Student student1.Name = "伊藤健二" student1.Year = 3 student1.Club = "サッカー" fmt.Println(student1) // {伊藤健二 3 サッカー} }
typeという予約語は既存の型や型リテラルに別名を付けて新しい型を定義できます。これを使って上の例では構造体からStudentという新しい型を作りました。そしてStudent型から構造体を作成してsetudent1に代入しています。
これでオブジェクト指向におけるクラスとインスタンスのような関係を再現できます。
student2 := Student{ Name: "松島花子" Year: 2 Club: "バレーボール" } fmt.Println(student2) // {松島花子 2 バレーボール}
このようにtypeを使って定義した構造体でも値で初期化する事ができます。
構造体でJSONを扱う
GoでJSONを扱うには構造体を使い、JSONのデータをフィールドに格納していきます。
import ( "json" ) type TeacherJSON struct { Name string `json:"name"` Age uint8 `json:"age"` Address string `json:"address"` } func DealJson(){ jsondata := []byte(` { "name": "岡崎一郎", "age": 45, "address": "東京都世田谷区〇〇一丁目" } `) var result TeacherJSON json.Unmarshal(jsondata, &result) fmt.Println(result) // {岡崎一郎 45 東京都世田谷区〇〇一丁目} }
&の使い方についてはポインタの項で説明します。
json.Unmarshal()関数を使う事で構造体にJSONデータを格納できます。ここで[]byte(データ)のようにかくとデータをバイナリデータ(生データ)に変換する事ができます。Unmarshal()関数の第一引数がbyte型でないといけないので変換します。
構造体の埋め込み
構造体の中に別の構造体を埋め込む事ができます。これによってオブジェクト指向におけるクラスの継承のような事が可能になります。
type EnbededAddress struct{ Prefecture string City string Address string } type Teacher struct{ Name string Age uint8 EnbededAddress } func EnbededStructure() { var teacher1 Teacher teacher1.Name = "木下雅美" teacher1.Age = 27 teacher1.Prefecture = "神奈川県" teacher1.City = "川崎市" teacher1.Address = "宮前区〇〇二丁目" fmt.Println(teacher1) // {木下雅美 27 {神奈川県 川崎市 宮前区〇〇二丁目}} }
方法は簡単で親となる上位構造体の定義の中で子となる下位構造体をフィールドとして指定するだけです。参照の際はいちいち下位構造体をしてしなくても、親構造体名.子構造体のフィールド名 で参照できます。
参照型
プログラム上の変数あるいは状態を間接的に参照するので参照型です。参照型にはポインタやスライス、マップ、チャネル、関数があります。
ポインタ
ポインタとは変数などのオブジェクトがメモリ上のどの位置に格納されているかという情報(アドレス)を参照・保持しているものです。 ポインタの詳しい説明はこちらから↓
https://wa3.i-3-i.info/word12814.html
Goでも変数のポインタを取得したり、ポインタが参照している変数の値を取得したりできます。
var ptr *int x :=10 ptr = &x fmt.Println("ポインタ変数ptrが指し示す変数xのアドレス =", ptr) // ポインタ変数ptrが指し示す変数xのアドレス = 824634318856 fmt.Printf("x =%d, ポインタ変数ptrが指し示す変数xの値 =%d", x, *ptr) // x = 10, ポインタ変数ptrが指し示す変数xの値 = 10
2つの記号を使います。*(アスタリスク)と&(アンパサンド)です。
ポインタ型を指定するにはvar 変数名 *データ型
とする事で、「そのデータ型の変数を参照する」ポインタ変数を定義できます。
変数 → アドレス
変数からその変数のアドレスを取得するには&を使って&x
です。
ポインタ変数 → 参照している変数の値
逆にポインタ変数から参照している変数の値を取得するには*を使って*ptr
です。
func pls(x *int) { return *x + *x } x = 10 fmt.Println("x + x =", pls(&x)) // x + x = 20
もちろんこのように関数の引数にポインタを渡すこともできます。
スライス
配列が固定長なのに対し、スライス可変長でデータの出し入れが柔軟なデータ型です。
配列からスライス作成
array := [5]string {"あ", "い", "う", "え", "お"} slice1 := array[:] // 全ての要素を持ったスライス fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice1, len(slice1), cap(slice1)) // 参照範囲=["あ" "い" "う" "え" "お"], 長さ= 5, 容量= 5 slice2 := array[1:4] // インデックスの1 ~ 3の要素を持ったスライス fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice2, len(slice2), cap(slice2)) // 参照範囲=["い" "う" "え"], 長さ= 3, 容量= 4 slice3 := array[:4] // インデックスの0 ~ 3の要素を持ったスライス fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice3, len(slice3), cap(slice3)) // 参照範囲=["あ" "い" "う" "え"], 長さ= 4, 容量= 5
Goもインデックスは0から始まります。配列名[インデックス範囲] のように指定することでその範囲のスライスを作成できます。 [1:4]などと指定した場合、4までは参照されず4 - 1つまり3までが参照されます。
長さと容量
スライスは長さと容量という2つの数を持っています。長さとはスライスの要素数で、容量とはスライスの最初の要素から数えた、元の配列の要素数です。それぞれ組み込み関数のlen()とcap()で調べる事ができます。
ここでスライスに容量を超えて要素を追加してみます。
array := [5]string {"a", "b", "c", "d", "e"} slice1 := array[:] slice2 := append(slice1, "f") slice3 := append(slice2, "g") fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice2, len(slice2), cap(slice2)) // 参照範囲=["a" "b" "c" "d" "e" "f"], 長さ=6, 容量=10 fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice3, len(slice3), cap(slice3)) // 参照範囲=["a" "b" "c" "d" "e" "f" "g"], 長さ=7, 容量=10
スライスへの値の追加は組み込み関数のappend()を使います。
ご覧のように容量を超えて要素を追加すると要領は元の2倍になります。
裏の動作としては新たにメモリ領域を拡張して全ての要素をそこへコピーし、appendされた分の要素も追加します。
スライスに別のスライスの要素を全て追加することもできます。
slice1 := make([]int, 3) slice2 := make([]int, 2) slice2 = append(slice2, 100) slice2 = append(slice2, 200) slice3 := append(slice1, slice2...) fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice3, len(slice3), cap(slice3)) // 参照範囲=[0 0 0 100 200], 長さ=5, 容量=6
append(追加するスライス, 追加されるスライス...) と書きます。
普通に作成・初期化
var slice[]int slice = append(slice, 100) fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d ¥n", slice, len(slice), cap(slice)) // 参照範囲=[100], 長さ=1, 容量=1
var 変数名 []型 で新しいスライスを作成できます。
slice := []int {10, 20, 30} fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d ¥n", slice, len(slice), cap(slice)) // 参照範囲=[10 20 30], 長さ=3, 容量=3
また、[]型 {初期値} と書くと初期化することもできます。
make関数で作成
slice1 := make([]int, 3) fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice1, len(slice1), cap(slice1)) // 参照範囲=[0 0 0], 長さ=3, 容量=3 slice2 := make([]int, 3, 5) fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d", slice1, len(slice1), cap(slice1)) // 参照範囲=[0 0 0], 長さ=3, 容量=5
make関数で作成する場合、make([]型, 長さ, 容量) と書きます。なお容量を省略した場合は要領は長さと同じになります。
make関数ではスライスの他に、マップとチャネルも作成する事ができます。
スライスのコピー
slice1 := []int {100, 200} slice2 := []int {10, 20 30} count := copy(slice2, slice1) fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d ,コピー件数=%d", slice2, len(slice2), cap(slice2), count) // 参照範囲=[100 200 30], 長さ=3, 容量=3,コピー件数=2
スライスに別のスライスの要素をコピーするにはcopy()関数を使います。第一引数がコピー先スライスを、第二引数がコピーされるスライスです。戻り値はコピー先スライスの要素のうち幾つがコピーによって変更されたかの数が帰ってきます。上の例の場合、インデックス0と1がコピーされた要素に置き換わったので、戻り値は2です。
slice1 := []int {100, 200} slice2 := []int {10, 20 30} count := copy(slice2[2:], slice1) fmt.Printf("参照範囲=%v, 長さ=%d, 容量=%d ,コピー件数=%d", slice2, len(slice2), cap(slice2), count) // 参照範囲=[10 20 100], 長さ=3, 容量=3,コピー件数=1
このようにコピー先のスライスを飛び出すように範囲を指定することもできますが、append関数のように容量が増えることはなく、飛び出した分は無視されます。
要素の削除
Goにはスライスから要素を削除する関数などは標準で組み込まれていません。そのため自分でその関数を作る必要があります。
func DeleteSlice(slice []int, i int) []int { slice := append(slice[:i], slice[i:]...) newSlice := make([]int, len(slice)) copy(newSlice, slice) return newSlice }
この関数は第一引数にスライスを、第二引数にインデックスをとります。そしてインデックス i - 1までと、i + 1からのスライスをくっ付けて新しいスライスに要素をコピーします。
slice := []int {1, 2, 3, 4, 5} fmt.Printf("削除前:値=%v,長さ=%d,容量=%d", slice, len(slice), cap(slice)) // 削除前:値=[1 2 3 4 5],長さ=5,容量=5 slice = DeleteSlice(slice, 1) fmt.Printf("削除後:値=%v,長さ=%d,容量=%d", slice, len(slice), cap(slice)) // // 削除後:値=[1 3 4 5],長さ=4,容量=4
ここでappend後のsliceをそのまま返すのではいけないのかという疑問があがるかもしれませんが、それをすると削除後のスライスの容量が4ではなく5のままになってしまい冗長なのです。
マップ
マップは配列やスライスと違い、キーを使ってデータ型を整理します。Pythonの辞書、Rubyのハッシュにあたります。
make関数で作成
map1 := make(map[int]string, 3) map1[1] = "りんご" map1[2] = "ゴリラ" map1[3] = "ラッパ" fmt.Printf("値=%v, 長さ=%d", map1, len(map1)) // 値=map[1:りんご 2:ゴリラ 3:ラッパ], 長さ=3
マップもmake関数で作成できます。その際はmake(map[キーの型]値の方, 容量) と書きます。なお容量の指定は省略できます。
値の参照は、マップ名[キー] で出来、代入も可能です。
初期化
var map2 = map[string]string{"名前": "まさお", "email": "masao123@gmail.com"} map2["住所"] = "群馬県" fmt.Printf("値=%v, 長さ=%d", map2, len(map2)) // 値=map[email:masao123@gmail.com 住所:群馬県 名前:まさお], 長さ=3
make関数を使わずとも、map[キーの型]値の型{初期値} と書くことでマップを初期化して作成できます。もちろんその後キーと値を追加することも可能です。
rangeにかける
マップをfor文の繰り返し処理でrangeにかけたときの挙動を説明します。
var students = map[int]string{1: "佐藤健吾", 2: "伊藤かなえ", 3: "下田良美"} for key, value := range students{ fmt.Printf("キー=%d, 値=%s", key, value) } /* キー = 3, 値 =下田良美 キー = 1, 値 = 佐藤健吾 キー = 2, 値 = 伊藤かなえ */
配列の場合、rangeはインデックスと要素を返していましたが、マップの場合はキーと値を返します。
また配列はインデックスで昇順にソートされているのに対し、マップはキーで値を識別するためソートがされていません。そのため上の例のようにfor文で取り出しても初期化した時とは違う順番で取り出されます。
キーの有無確認
teachers := make(map[int]string) teachers[1] = "山田剛" teachers[2] = "遠藤正樹" teachers[3] = "加藤美紀" value, exist := teachers[2] if exist { fmt.Println(value) // 遠藤正樹 }else { fmr.Println(exist) }
マップ名[キー]で参照すると、valueの他にexistという値も得る事ができます。これはキー(と値)が存在するかの真偽値で、上の例の場合は存在しているのでtrueです。存在しない場合、valueは空に、existはfalseになります。
キー(と値)の削除
teachers := make(map[int]string) teachers[1] = "山田剛" teachers[2] = "遠藤正樹" teachers[3] = "加藤美紀" delete(teachers, 2) fmt.Printf("値=%v,長さ=%d ¥", names, len(names)) // 値=map[1:山田剛 3:加藤美紀],長さ=2
キーと値を削除するにはdelete()関数が用意されています。delete(マップ名, キー) と書きます。
チャネル
チャネルは並行実行しているゴルーチン間を接続するパイプとして機能します。チャネルや並行処理、ゴルーチンは別記事で解説します。
関数
関数についても別記事で解説します。
インターフェース型
インターフェース型とは「メソッドにおける引数や戻り値の型だけを定義した」型で、オブジェクト指向でいうポリフォーリズムを実現する事ができます。こちらも別記事で解説します。
Go言語 基礎講座 #4 繰り返し for
目次
for文
for i := 1; i <10; i++ { fmt.Println(i) } /* 1 2 3 4 5 6 7 8 9 */
for文は基本は for 変数定義; 条件式; 変数処理{ 処理 } という書き方をします。
while文の代わり
i := 1 for i < 10{ fmt.Println(i) i++ } /* 1 2 3 4 5 6 7 8 9 */
このようにforの後ろに条件式だけを置く書き方もできます。Goにはwhile文がないので、while文の代わりにこの書き方を使います。この場合繰り返し処理の中で変数をインクリメントするなりの処理を開く必要があります。
break・continue
i := 1 for i <= 15 { if i == 12 { break } fmt.Println(i) i =* 3 } /* 1 3 6 9 */
breakはfor文の繰り返しから抜け出すことができます。そのためbreak以後の処理は実行されません。基本的にif文などと組み合わせて、ある条件のときに繰り返しから抜けるなど処理に使われます。
i := 0 for { if i % 3 == 0 { fmt.Peintln(i) i++ }else{ i++ continue } if i > 15 { break } } /* 3 6 9 12 15 18 */
continueは繰り返し内の処理を飛ばすことが出来ます。そのためcontinue以後のその回の繰り返し内の処理は実行されず、次の回に移行します。
またGoではforは条件など何もなくてもfor { 処理 }と書くことも出来ます。その場合は上の例のように、繰り返し内の処理でインクリメント等の変数の処理と繰り返しを終了する処理を書いておく必要があります。
range
rangeは文字列や配列、スライス(データ型は後の講座で解説)などのシーケンスデータに対して繰り返し処理を行うのに便利な記法です。
str := "abcde" for index, value := range str { fmt.Printf("index = %d, value = %s", index,string(value)) } /* index = 0, value = a index = 1, value = b index = 2, value = c index = 3, value = d index = 4, value = e */
for インデックス, 値 := range シーケンス { 処理 } のように書きます。繰り返し処理でインデックスと値を変数として利用できます。
Go言語 基礎講座 #3 条件分岐 if・switch
目次
if文
var a int = 10 if a == 10 { fmt.Println("a =", a) // a = 10 }
このように if 条件式{ 処理 } という文法を使う。
if a == 20 { fmt.Println("a = 20") }else if a == 30{ fmt.Println("a = 30") }else{ fmt.Println("other") // other }
代入文を書ける
Goに特有な記法としてif文の条件式の前に代入文を置くことができます。
if err := function(); err != nil { // error handling }
上記の例は関数funcrion()で例外が発生した時のエラーハンドリングです。Go言語には標準で例外処理が実装されていないので、このようにして例外処理を行います。
if a := f(); a == nil { fmt.Plintln("a is nil") }else if b = g(a); b ==nil { fmt.Plintln("a and b are nil") }else{ fmt.Plintf("a=%d, b=%d", a, b) }
このようにelse ifでも代入文を使うことが出来ます。また一度代入した変数はif文の中では後でも使えます。上の例ではifで代入定義した変数aをelse ifでも使っています。
switch文
var a int = 30 switch a { case 10: fmt.Println("a = 10") case 20: fmt.Println("a = 20") case 30: fmt.Println("a = 30") // a = 30 default: fmt.Println("other") }
Goにはswitch文が用意されています。switch文は条件分岐の条件が複数ある場合に便利な記法です。switchの後に条件式を置き、case:の条件式と一致すればその後の処理が実行されます。どの条件式とも一致しなかった場合default:の後の処理が実行されます。また一つcaseが実行されるとそのswitch文は終了するので明示的にbreakを書く必要はありません。
fallthrough
switch a { case 10: fmt.Println("a = 10") case 20: fmt.Println("a = 20") case 30: fmt.Println("a = 30") // a = 30 fallthrough case 40: fmt.Println("a = 40") // a = 40 fallthrough case 50: fmt.Println("a = 50") // a = 50 fallthrough default: fmt.Println("other") // other }
fallthroughを使うとcaseを実行した後でも処理を継続できます。その場合fallthrough以後のcase条件式は見られません。(a=30ではないですが40,50,otherが出力されていますよね。)
Go言語 基礎講座 #2 変数・定数
目次
変数への代入
var a int a = 10 b := 20 fmt.Println("a =", a) // a = 10 fmt.Println("b =", b) // b = 20
2種類の代入方法があります。
1つ目は先に変数を定義してから値を代入する方法で、2つ目は定義と値の代入を同時にする方法です。1つ目は明示的にデータ型を指定するので、コンパイル時に型の不整合を検出できたり、保守性が高まるなどのメリットがあります。対して2つ目の方法はコードの記述量が減ったり、コードの変更に強いなどのメリットがあります。
どちらの方法を採るかは開発の方針に従ってください。
複数値の代入
var a, b, c int a, b, c = 10, 20, 30 fmt.Printf("a=%d, c=%d, c=%d ¥n", a, b, c) // a=10, b=20, c=30
このように複数の値の代入もできます。
代入法1の応用/インクリメント
var a, b int = 10, 20 a++ b-- fmt.Println("a =", a) // a = 11 fmt.Println("b =", b) // b = 19
代入1の方法の応用で、変数定義と同時に値も代入してしまうことが出来ます。変数の値のインクリメントはa += 1でも可能ですが、a++と書くことでより省略して書くことが出来ます。デクリメントも同様です。
定数の定義
const A = 100 fmt.Println("A =", A) // A = 100
Goの定数は型指定をしなくても定義が可能です。
var a int64 = A fmt.Println("a =", a) // a = 100
なのでこのように変数に定数の値を代入しても型チェックのエラーにならなりません。
const B int64 = 200 fmt.Println("B =", B) // B = 200
もちろん型を指定することもできます。
複数同時定義
const ( A int = 10 B C ) fmt.Printf("A=%d, B=%d, C=%d", A, B, C) // A=10, B=10, C=10
定数も複数を同時に定義することができます。その際、初めの定数だけに型指定・代入を行うと、以降すべての定数に同じ型と代入値が適用されます。
const ( A int = 10 B = 11 C = 12 ) fmt.Printf("A=%d, B=%d, C=%d", A, B, C) // A=10, B=11, C=12
ちなみにこのように定義すると、Aはint型ですがBとCは型指定されないのでuntypedになります。
自動インクリメント iota
const ( zero = iota one two ) fmt.Printf("zero=%d, one=%d, two=%d", zero, one, two) // zero=0, one=1, two=2
Goには定数のためのiotaという記法があります。定数をまとめて定義する際に一番初めの定数にiota記法を使うと以降の定数に掛けて値がインクリメントされて代入されます。ちなみに最初の値は自動で0になります。
const ( A = 10 B = iota + 10 C ) fmt.Printf("A=%d, B=%d, C=%d", A, B, C) // A=10, B=11, C=12
iotaの応用で、上記のようにiotaを定義の途中から使うと代入される値は0からではなく1からになります。これは間違えやすいので要注意です。
const ( A = iota * iota B C ) fmt.Printf("A=%d, B=%d, C=%d", A, B, C) // A=0, B=1, C=4
iotaをこのように使うこともできます。
Go言語 基礎講座 #1 値の出力・演算子
目次
ターミナルへの出力
package learngo import ( "fmt" ) fmt.Plintln("Hello World!") fmt.Plintln(1) fmt.Plintln("I am", 20, "years old")
fmtは入出力に関する標準パッケージで、Plintln()メソッドを使うことでターミナルに値を書き出すことができます。
コメントアウト
// これはコメントです /*改行を含んだ コメントアウトはこれを使おう*/
数値演算
fmt.Plintln("3 + 6 = ", 3 + 6) // 3 + 6 = 9 fmt.Plintln("9 - 3 = ", 9 - 3) // 9 - 3 = 6 fmt.Plintln("3 * 6 = ", 3 * 6) // 3 * 6 = 18 fmt.Plintln("9 / 3 = ", 9 / 3) // 9 / 3 = 3 fmt.Plintln("12 % 5 = ", 12 % 5) // 12 % 5 = 2
%は余りを計算する演算子で、12を5で割った余りは2です。
ビット演算
fmt.Plintln("2 << 1 =", 2 << 1) // 2 << 1 = 4 fmt.Plintln("2 >> 1 =", 2 >> 1) // 2 >> 1 = 1 fmt.Plintln("2 & 5 =", 2 >> 5) // 2 & 5 = 0 fmt.Println("2 | 5 =", 2 | 5) // 2 | 5 = 7 fmt.Println("2 ^ 5 =", 2 ^ 5) // 2 ^ 5 = 7 fmt.Println("2 &^ 5 =", 2 &^ 5) // 2 &^ 5 = 2
<<は左シフト、<<は右シフト、&はビット積、| はビット和、&^はビットクリア演算子です。ビットクリア演算子とはx &^ yと言う式において、xの0にしたいビットをyで1にして指定しておけばそのビットは0と出力されると言うものです。
ビット演算の詳しい説明はこちらから→
https://wa3.i-3-i.info/word11655.html
比較演算
fmt.Println("3 < 1 =", 3 < 1) // 3 < 1 = false fmt.Println("3 > 1 =", 3 > 1) // 3 > 1 = true fmt.Println("3 <= 1 =", 3 <= 1) // 3 <= 1 = false fmt.Println("3 >= 1 =", 3 >= 1) // 3 >= 1 = true fmt.Println("3 == 1 =", 3 == 1) // 3 == 1 = false fmt.Println("3 != 1 =", 3 != 1) // 3 != 1 = false
>は大なり、>=は大なりイコール、<は小なり、<=は小なりイコール、==は等しい、!=は等しくないことを表す演算子です。
論理演算
fmt.Println("true && true =", true && true) // true && true = true fmt.Println("true && false =", true && false) // true && false = false fmt.Println("ture || true =", true || true) // true || true = true fmt.Println("ture || false =", true || false) / // true || false = false fmt.Println("! ture =", ! true) // ! true = false fmt.Println("! false =", ! false) // ! false = true
Go言語 基礎講座 #0 環境構築
早速少し話がそれますが、自分はGo言語のJetBrains製IDEであるGolandを使っていてGoの新規プロジェクトの作成などを勝手にしてくれるのでおすすめです。学生の方でしたら無料で使えるライセンスがあるのでぜひ確認してみてください↓
目次
インストール
前提環境
- Mac OS
- Homebrewがインストール済
インストール
$ brew install go
これでインストール完了です。
$ go version go version go1.13.6 darwin/amd64
バージョン確認をして、ちゃんとインストールされたことを確かめましょう。
GOPATHの設定
export GOPATH=/Users/YOUR_USER_NAME/go export PATH=$GOPATH/bin:$PATH
.bash_profileに↑を追加。GOPATHはインストールするGo関連の外部ライブラリなどが入る場所です。Goのインストール後はこの設定をしなければいけません。
$ source ~/.bash_profile
.bash_profileをアクティベートして設定を適用します。 今回があくまで例として~/goをGOPATHとして設定しましたが、他の場所でも構いません。 どこに設定すべきかこの記事で解説されています↓
以上でインストール作業は完了です!
新規プロジェクトの作成
$ mkdir PROJECT_NAME // ~/go/src以下で $ cd PROJECT_NAME $ go mod init github.com/GITHUB_USER_NAME/PROJECT_NAME go: creating new go.mod: module github.com/GITHUB_USER_NAME/PROJECT_NAME
これで新規プロジェクトが~/go/src以下に作成されました。go mod initコマンドを行わないとGo Moduleプロジェクトと認識されなくなります。
Depのインストール
DepはGoのパッケージ依存関係管理ツールです。
$ go get -u github.com/golang/dep/cmd/dep
これでインストールは完了。
$ dep init
これでDepを初期化し、このプロジェクトで利用できる状態にします。
Depのメリット
Depのメリットは外部パッケージのインストールの手間が省けると言うことです。
Depがないと上のようにいちいちgo get ~~とコマンドを打ち込まないといけませんが、Depだと...
imoprt( "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" )
このようにコード内にこのパッケージを使いますよーと言う記述だけで、あとは以下のコマンドを実行すれば全てのパッケージを一括インストールしてくれます。
$ dep ensure
ディレクトリ構成
Goはパッケージ(ディレクトリ)レベルで名前空間を構成しています。そしてプロジェクトルート(~/go/src/PROJECT_NAME)はmainパッケージといい、mainパッケージ内のmain.goから他のパッケージを参照するなどしてプログラム全体を実行します。
なので基本はgo run(Goの実行コマンド)はmain.goに対してのみ行います。
PROJECT_NAME/ | ---main.go | ---learngo/ | | | ---learn.go --
例えば、この基礎講座のためにlearngo/と言うパッケージを作り、その中にlearn.goと言うファイルを作ってください。この中にこれから学ぶGoのプログラムを書き込んで行きます。(関数など)
package main import ( learngo "PROJECT_NAME/learngo" ) func main() { learngo.SOME_FUNCTION() }
main.goこのように書きます。Goファイルでは必ず一番上でpackageを明示してください。次にパッケージをインストールします(今回はlearngo)。そしてmain関数を作成します。main.goを実行するとこのmain関数が実行されます。
今後この講座で紹介する内容はlearn.go内の適当な名前の関数内に実装し、main.goから呼び出して実行してください。
// learn.go func SOME_FUNCTION(){ <講座で紹介する内容> }
以上でGoでプログラミングをする準備が整いました。次回からGoの文法などを説明していきます。