Goの制御構文について

Goの制御構文は、非常にシンプルにデザインされており、最低限の予約語で記述できるようになっています。そのため、記述方法にはたくさんのバリエーションがあるので、それについてまとめていきます。

##if文
まず、条件分岐を制御する**if文**です。if文の基本形は下記の通りです。

```
if 条件式 {
   条件成立時の処理
} else if 条件式 {
   最初の条件式が成立しなかった場合の処理
} else {
   すべての条件式が成立しなかった場合の処理
}
```

ちなみに、条件式はbool値である必要があります。

##簡易文を設定したif文
条件式の前をセミコロンで区切り、簡易文を設定することができます。

```go
x, y := 5, 10

if a := x * y; a == 50 {
   fmt.Println("変数aは50です")
} else {
   fmt.Println("変数aは謎です")
}  //変数aは50です
```

また、if文を利用してエラー処理も実現できます。

```go
import (
   "os"
   "log"
)

func test() error {
   _, e := os.Open("test.txt")
   return e
}

func main() {
   if err := test(); err != nil {
      log.Fatal(err)  //test.txtというファイルは存在しないので、エラ〜メッセージを出力
   }
}
```
[前回の記事(Goの関数について)](https://qiita.com/noa-1129/items/589d514b4eda0cf9c632 "Qiita")でも書きましたが、今回は関数を2つに分けて、エラー処理を行なっています。簡単に説明すると、errorインターフェースを戻り値にとり、osパッケージで架空のファイル"test.txt"を開く関数を定義しています。その関数をmain関数で受け取り、エラーの判別を行なっています。

##for文
Goにはループを記述する構文がforのみとなっています。下記は特に指定をしていない裸のforです。forでは、何の指定もしない場合には、無限ループを実行します。

```go
for {
   fmt.Println("裸のfor")  //無限ループ
}
```

##よくみるfor文
初期化文・条件式・後処理分の３つを、セミコロンで区切り並べることで、よくみる典型的なfor文を書くことができます。

```go
for a := 0; a < 10; a++ {
   fmt.Println(a)  //0 1 2 3 4 5 6 7 8 9
}
```

##範囲節を使用したfor文
**予約語range**を使用して、範囲式を定義したfor文です。

```go
tests := [3]string{ "test1", "test2", "test3" }

for i, a := range tests {
   fmt.Printf("[%d]%s\n", i, a)  //[0]test1
                                 //[1]test2
                                 //[2]test3
}
```

このプログラムは、iで配列のインデックスを、変数aには変数testsの値を代入し、配列と、インデックスに対した繰り返しのループ処理が行われています。

また、rangeを使用して文字列型に対した繰り返し処理も可能ですが、反復値が**rune型**になるところから、Unicodeにおける文字コードとして出力されます。さらに、文字列のインデックスは、UTF-8でエンコードされたコードポイントごとに反復されるため、文字のコードポイントによってインデックスの増分量は差異があります。

##switch文
switch文は、任意で簡易文を置き、式を評価して分岐処理を行う処理です。また、switch文には**式によるswitch文**と**型によるswitch文**の2つの処理があります。

```
switch 簡易文(;) 式 {
   各分岐処理...
}
```

##式によるswitch文
下記は、式を使用したswitch文です。

```go
switch a := 2; a {
case 1:
   fmt.Println("1です")
case 2: 
   fmt.Println("2です")  //出力
case 3: 
   fmt.Println("3です")
case 4: 
   fmt.Println("4です")
case 5: 
   fmt.Println("5です")
default: 
   fmt.Println("謎の値")
```

変数aに2を代入した式を評価し、分岐処理を行なっています。

##式を設定したcase
caseに式を設定し、分岐処理を行うことも可能です。

```golang
b, c := 1, 5

switch a := 5; a {
case b + c:
   fmt.Println("足し算")
case b - c:
   fmt.Println("引き算")
case b * c:
   fmt.Println("かけ算")  //出力
case b / c:
   fmt.Println("わり算")
```

変数aに５を代入し、それぞれcaseに設定した式から分岐処理を行なっています。

##型によるswitch文
型アサーションと組み合わせて分岐処理を行うことで、手軽に型を使用したswitch文を書くことができます。

```golang
var x interface{} = 5  

switch a := x.(type) {  //型アサーションの式
case int:
   fmt.Println(a * a)  //出力 //25
case bool:
   fmt.Println("bool型です")
case string:
   fmt.Println("string型です")
default:
   fmt.Println("謎の値")
}
```

このプログラムは、すべての型と互換性のあるinterface{}型で変数を定義し、その値を、**型アサーション**という動的に型をチェックする機能を利用した式から、分岐処理を行なっています。

##breakとcontinue
ループ処理の中断の機能として、**break文**
残処理をスキップし、次のループ処理へ継続する機能として、**continue文**があります。

```break.go
a := 0
for {
   fmt.Println(a)
   a++
   if a == 10 {
      break  //変数aが10になったら処理を中断
   }
}
```

```continue.go
for a := 0; a < 10; a++ {
   if a == 5 {
      continue
   }
   fmt.Println(a)  //変数aが5になったら、その値をスキップする
}
```

##ラベル付き文
breakやcontinue文にラベルを組み合わせることで、複数ネストしていた場合でも、任意の位置にジャンプすることができます。**ラベル:**の形で位置を定義します。

```break.go
a := 5
EXIT:
   for {
      for {
         for {
            fmt.Println(a * a)  //25
            break EXIT
         }
         fmt.Println(a + a)  //出力されない
      }
      fmt.Println(a - a)  //出力されない
   }
   fmt.Println("DONE")  //DONE
```

ラベルEXITを定義して、break EXITのところで脱出しています。

次に、continue文を使用した場合です。

```continue.go
SKIP:
   for a := 1; a <= 5; a++ {
      for b := 1; b <= 5; b++ {
         if b > 1 {
            continue SKIP
         }
         fmt.Printf("a = %d, b = %d\n", a, b)  //a = 1, b = 1
      }                                        //a = 2, b = 1
      fmt.Println("ここには来ません")              //a = 3, b = 1
   }                                           //a = 4, b = 1
                                               //a = 5, b = 1
```     

forが二重ネストしてあり、内側のforに対してbが1より多きい場合に、ループをスキップするようになっています。つまり、内側のループは、1回しか実行されていないということがわかります。

##予約語defer
**defer**は、関数が終了したタイミングで実行される式を登録できます。

```golang
func main() {
   defer fmt.Println("defer1")
   defer fmt.Println("defer2")
   fmt.Println("これが先")  //これが先
}                         //defer2
                          //defer1
```

注意点としては、後に登録した式から出力されるといったところがあります。

また、無名関数を利用することで、deferを複数登録することができます。

```golang
defer func() {
   fmt.Println("defer1")
   fmt.Println("defer2")
   fmt.Println("defer3")
}()
```

次にdeferは、ファイルのオープン処理に際に、処理漏れを防ぐためにファイルをクローズするといった使い方もできます。

```golang
ファイルのオープン処理...
}

defer file.Close()
```

##ゴルーチン
go文を使用することで、並行処理される**ゴルーチン**を生成することができます。

```golang
func test() {
	tests := [...]string{ "test1", "test2", "test3" }
	for i, a := range tests {
		fmt.Printf("[%d]%s\n", i, a)
	}
}

func main() {
	go test()
	mains := [3]string{ "main1", "main2", "main3" }
	for i, b := range mains {
		fmt.Printf("[%d]%s\n", i, b)
	}
} 
```

実行結果

```
[0]main1
[1]main2
[2]main3
[0]test1
[1]test2
[2]test3
```

main関数内で、```go test()```とすることで、test関数のゴルーチンが生成され、不規則に並行処理が行われます。ちなみに、配列型を同じにすると上手く動作しないので注意が必要です。

また、無名関数を使用しても同じように動作します。

##最後に
今回は、制御構文についてまとめてみました！制御構文は頻出する処理だと思うので、しっかり理解したいところではありますね〜

##参考文献
著者　松尾愛賀
[翔泳社　スターティングGo言語](https://www.shoeisha.co.jp/search?search=%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0Go%E8%A8%80%E8%AA%9E)