1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Goの制御構文について

Last updated at Posted at 2020-11-03

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文があります。

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は、関数が終了したタイミングで実行される式を登録できます。

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言語

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?