Go言語 基礎講座 #5 データ型

f:id:shinta2000ttt:20200722180639p:plain この記事は辞書のように使っていただければと思っています。

目次

データ型の分類

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

複素型

複素数を表現する型です。

  • complex64 : 虚数部と実数部をfloat32で表現。
  • complex128 : 虚数部と実数部を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

f:id:shinta2000ttt:20200722180639p:plain

目次

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

f:id:shinta2000ttt:20200722180639p:plain

目次

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 変数・定数

f:id:shinta2000ttt:20200722180639p:plain

目次

変数への代入

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 値の出力・演算子

f:id:shinta2000ttt:20200722180639p:plain

目次

ターミナルへの出力

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

&&は論理積、||は論理和、!は否定を表す演算子です。 論理演算に関する詳しい説明はこちらから→

【5分で覚えるIT基礎の基礎】あなたは論理演算がわかりますか? 第1回 | 日経クロステック(xTECH)

Go言語 基礎講座 #0 環境構築

f:id:shinta2000ttt:20200722180639p:plain

早速少し話がそれますが、自分はGo言語のJetBrains製IDEであるGolandを使っていてGoの新規プロジェクトの作成などを勝手にしてくれるのでおすすめです。学生の方でしたら無料で使えるライセンスがあるのでぜひ確認してみてください↓

www.jetbrains.com

目次

インストール 

前提環境

  • 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として設定しましたが、他の場所でも構いません。 どこに設定すべきかこの記事で解説されています↓

Go言語の開発環境セットアップ - Qiita

以上でインストール作業は完了です!

新規プロジェクトの作成

$ 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の文法などを説明していきます。