Goの制御構文は、非常にシンプルにデザインされており、最低限の予約語で記述できるようになっています。そのため、記述方法にはたくさんのバリエーションがあるので、それについてまとめていきます。
##if文
まず、条件分岐を制御するif文です。if文の基本形は下記の通りです。
if 条件式 {
条件成立時の処理
} else if 条件式 {
最初の条件式が成立しなかった場合の処理
} else {
すべての条件式が成立しなかった場合の処理
}
ちなみに、条件式はbool値である必要があります。
##簡易文を設定したif文
条件式の前をセミコロンで区切り、簡易文を設定することができます。
x, y := 5, 10
if a := x * y; a == 50 {
fmt.Println("変数aは50です")
} else {
fmt.Println("変数aは謎です")
} //変数aは50です
また、if文を利用してエラー処理も実現できます。
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の関数について)でも書きましたが、今回は関数を2つに分けて、エラー処理を行なっています。簡単に説明すると、errorインターフェースを戻り値にとり、osパッケージで架空のファイル"test.txt"を開く関数を定義しています。その関数をmain関数で受け取り、エラーの判別を行なっています。
##for文
Goにはループを記述する構文がforのみとなっています。下記は特に指定をしていない裸のforです。forでは、何の指定もしない場合には、無限ループを実行します。
for {
fmt.Println("裸のfor") //無限ループ
}
##よくみるfor文
初期化文・条件式・後処理分の3つを、セミコロンで区切り並べることで、よくみる典型的なfor文を書くことができます。
for a := 0; a < 10; a++ {
fmt.Println(a) //0 1 2 3 4 5 6 7 8 9
}
##範囲節を使用したfor文
予約語rangeを使用して、範囲式を定義したfor文です。
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文です。
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に式を設定し、分岐処理を行うことも可能です。
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に5を代入し、それぞれcaseに設定した式から分岐処理を行なっています。
##型によるswitch文
型アサーションと組み合わせて分岐処理を行うことで、手軽に型を使用したswitch文を書くことができます。
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文があります。
a := 0
for {
fmt.Println(a)
a++
if a == 10 {
break //変数aが10になったら処理を中断
}
}
for a := 0; a < 10; a++ {
if a == 5 {
continue
}
fmt.Println(a) //変数aが5になったら、その値をスキップする
}
##ラベル付き文
breakやcontinue文にラベルを組み合わせることで、複数ネストしていた場合でも、任意の位置にジャンプすることができます。**ラベル:**の形で位置を定義します。
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文を使用した場合です。
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は、関数が終了したタイミングで実行される式を登録できます。
func main() {
defer fmt.Println("defer1")
defer fmt.Println("defer2")
fmt.Println("これが先") //これが先
} //defer2
//defer1
注意点としては、後に登録した式から出力されるといったところがあります。
また、無名関数を利用することで、deferを複数登録することができます。
defer func() {
fmt.Println("defer1")
fmt.Println("defer2")
fmt.Println("defer3")
}()
次にdeferは、ファイルのオープン処理に際に、処理漏れを防ぐためにファイルをクローズするといった使い方もできます。
ファイルのオープン処理...
}
defer file.Close()
##ゴルーチン
go文を使用することで、並行処理されるゴルーチンを生成することができます。
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言語