2.ステートメント - iruma-tea/go_programming GitHub Wiki

2. ステートメント

条件分岐のif文や繰り返し処理のfor文など、Goの基本的なステートメント(文)について記載する。

2.1 if文で条件分岐の処理を実行しよう

Goで条件分岐を書く際には、「if」、「else」、「else if」を使う。

2.1.1 if文で条件分岐のプログラムを作ろう

if文は、「if 条件式 { }」と書いて、{ }内に条件に当てはまるときに実行する処理を書く。
次のコードは、変数numの値を2で割ったあまりが0の場合、「by 2」と表示するif文の処理です。
例えば、変数numが4の場合、2で割ったあまりは0なので、if文の中に入った「by 2」が表示される。

num := 4
if num%2 == 0 {
    fmt.Println("by 2")
}

else節について、if文の条件に当てはまらない場合の処理を作成するときには、if文の後にelse節を書く。

num := 5
if num%2 == 0 {
    fmt.Println("by 2")
} else {
    fmt.Println("else")
}

else if節について、if文とelse文の間に「else if」と書き、条件式と処理を書く

num := 9
if num%2 == 0 {
    fmt.Println("by 2")
} else if num%3 == 0 {
    fmt.Println("by 3")
} else {
    fmt.Println("else")
}

2.1.2 複数の条件がある場合

条件が複数ある場合、論理演算子を使う。複数の条件にすべて当てはまるかを判別した場合は、条件式を&&でつなげる

x, y := 10, 10
if x == 10 && y == 10 {
    fmt.Println("&&")
}

複数ある条件のうちいずれかに当てはまるかを判別した場合は、条件式を||でつなげる。

x, y := 10, 10
if x == 10 && y == 10 {
    fmt.Println("&&")
}

if x == 10 || y == 10 {
    fmt.Println("||")
}

2.1.3 if文の条件式で変数を宣言しよう

数値が2で割り切れるかどうかチェックする、by2という関数を作成する。関数by2の戻り値はstring型で、
引数で受け取った値を2で割ったあまりが0の場合は文字列「ok」、そうでない場合は、「no」を返す。

func by2(num int) string {
    if num%2 == 0 {
        return "ok"
    } else {
        return "no"
    }
}

func main() {
    result := by2(10)
    if result == "ok" {
        fmt.Println("great")
    }
}

上記のような変数へ代入とif文の条件を1行でまとめて書く場合、if文の後に変数を宣言し、;(セミコロン)でつなげて条件を書く

func by2(num int) string {
    if num%2 == 0 {
        return "ok"
    } else {
        return "no"
    }
}

func main() {
    result := by2(10)
    if result == "ok" {
        fmt.Println("great")
    }

    if result2 == by2(10); result2 == "ok" {
        fmt.Println("great2")
    }
}

なお、1行にまとめて書く場合、変数の値を以降のプログラムで使うことはできない。

2.2 for文で処理を繰り返し実行しよう

Goでの繰り返し処理にはfor文を使う。次の繰り返しに進むcontinue文や、繰り返しを抜けるbreak文なども他のプログラミング言語と同じ。

2.2.1 for文による繰り返し処理を作ろう

Goでのfor文は、forの後に「繰り返しで使う変数の初期化」「繰り返しを続ける条件」「繰り返すたびに実行する処理」を;(セミコロン)で区切って書く。

for i := 0; i < 10; i++ {
    fmt.Println(i)
}

2.2.2 continue文で次の繰り返しに進む処理を作ろう

continue文は、処理の途中で次の繰り返し処理に進むときに使う。

for i := 0; i < 10; i++ {
    if i == 3 {
        fmt.Println("continue")
        continue
    }
    fmt.Println(i)
}

2.2.3 break文で繰り返しを途中で抜けよう

break文は、繰り返しを中断してfor文から抜けたいときに使う。

for i := 0; i < 10; i++ {
    if i == 3 {
        fmt.Println("continue")
        continue
    }

    if i > 5 {
        fmt.Println("break")
        break
    }
    fmt.Println(i)
}

2.2.4 for文の省略記法を使おう

for文では、for後に「変数の初期化」、「繰り返しを続ける条件」、「繰り返すたびに実行する処理」を;(セミコロン)で区切って書きますが、このうち、
一つ目の「変数の初期化」と三つ目の「繰り返すたびに実行する処理」は省略が可能。

sum := 1
for ; sum < 10 ; {
    sum += sum
    fmt.Println(sum)
}
fmt.Println(sum)

上記、;(セミコロンは省略可能)

sum := 1
for sum < 10 {
    sum += sum
    fmt.Println(sum)
}
fmt.Println(sum)

上記、変数sumの値が増えない場合、無限ループとなってしまうので注意。
実際に無限ループを発生させたい場合は、以下のような記述で無限ループを発生させることができる。

for {
    fmt.Println("Hello")
}

2.2.5 rangeで繰り返しの処理を簡単に書こう

for文と一緒に使うと便利なのが、rangeです。
「python」、「go」、「java」という文字列を持つスライスを変数lに入れて、for文で文字列の中身を取り出す処理は以下のようになる。

l := []string{"python", "go", "java"}

for i := 0; i < len(l); i++ {
    fmt.Println(i, l[i])
}

上記、処理はrangeを使うともっと簡単に書ける。forの後に「i, v := range l」のように書くと、
変数iとvにそれぞれ、lのインデックス番号と中身を代入できる。

l := []string{"python", "go", "java"}

for i, v := range l {
    fmt.Println(i, v)
}

for文の中の処理でインデックス番後を使わないというときには、iを_(アンダースコア)に置き換えると、スライスの値だけを取り出すことができる。

l := []string{"python", "go", "java"}

for _, v := range l {
    fmt.Println(v)
}

次のようにして、マップのキーと値をrangeで取り出すこともできる

m := map[string]int{"apple": 100, "banana": 200}

for k, v := range m {
    fmt.Println(k, v)
}

マップのっきーだけを取り出したいときは、vを書かず次のようにする。

m := map[string]int{"apple": 100, "banana": 200}

for k := range m {
    fmt.Println(k)
}

マップの値だけを取り出したい場合は、「, v := range m」のように**kを(アンダースコア)にする**

m := map[string]int{"apple": 100, "banana": 200}

for _, v := range m {
    fmt.Println(v)
}

2.3 switch文で条件に応じた処理を実行しよう

2.3.1 switch文で条件ごとの処理を作ろう

switch文で変数osの値を判定していく。変数の値が「mac」の場合は、「Mac!!」、「windows」の場合は「Windows!!」と表示する。
それ以外の場合の処理は「Default!!」と表示する。

os := "mac"
switch os {
    case "mac":
        fmt.Println("Mac!!")
    case "windows":
        fmt.Println("Windows!!")
    default:
        fmt.Println("Default!!")
}

2.3.2 変数宣言とswitch文をまとめて書こう

変数への代入とswitch文は、1行でまとめて書ける。

func getOsName() string {
	return "mac"
}

func main() {
	switch os := getOsName(); os {
	case "mac":
		fmt.Println("Mac!!")
	case "windows":
		fmt.Println("Windows!!")
	}
}

2.3.3 条件式を書かないswitch文を作ろう

switch文に条件式を書かず、caseに判定条件を書く方法もある。

func main() {
	t := time.Now()
	fmt.Println(t.Hour())
	switch {
	case t.Hour() < 12:
		fmt.Println("Morning")
	case t.Hour() < 17:
		fmt.Println("Afternoon")
	}
}

2.4 defer文で処理を遅らせて実行しよう

deferを使うと、処理を遅らせて関数の最後に実行させることが可能。
例えば、ファイルを開く処理を実行した場合、ファイルを使い終わったら閉じる処理を入れる必要があるが、
ファイルを開いたときにdefer文で閉じる処理も入れておくとファイルの閉じ忘れを防ぐことができる。

2.4.1 defer文で処理を実行しよう

defer文は処理の遅延実行をする際に使う。defer文の後に遅延実行したい処理を書く。

func main() {
	defer fmt.Println("world")
	fmt.Println("hello")
}

2.4.2 defer文でファイルの閉じ忘れを防ごう

ファイルを読み込む際には最後にCloseで閉じる必要がある。
Openした直後にdefer文でCloseしておくことで閉じ忘れを防ぐことができる。

func main() {
	file, _ := os.Open("./lesson.go")
	defer file.Close()
	data := make([]byte, 100)
	file.Read(data)
	fmt.Println(string(data))
}

2.5 ログを出力しよう

ログ処理は、アプリケーションに障害が発生したときの原因究明などで必要になる。
ここでは、Goの標準ライブラリであるlogを使ったログ出力方法について、ファイルへの出力方法と合わせて簡単に説明する。

2.5.1 logパッケージでログを出力しよう

Goでログを出力するときは、logというパッケージを使用する。よく使われるのがlog.Println

  • log.Printf: フォーマットを指定することができる。(typeやvalue)
  • log.Fatalln: エラーの出力によく使われる。
    • log.Fatalln を実行するとプログラムを終了する。
  • log.Fatalf: フォーマットを指定することができる。(typeやvalue)
    • log.Fatalf を実行するとプログラムを終了する。

2.5.2 ログをファイルに書き込もう

  • LoggingSettingsという関数を作る。
    • string型の引数logFileを渡す。
    • ログファイルを書き込むためのファイルを、os.OpenFile関数を使って開く。
      • ファイルを開く際にフラグやモードを指定する。
        • |(パイプ)を使うと複数のフラグを指定することもできる
          • 「os.O_RDWR」(読み書き権限で開く)
          • 「os.O_CREATE」(ファイルがなければ作成する)
          • 「os.O_APPEND」(ファイルの内容を追記する)
        • モードには「0666」(どのユーザーもファイルの読み書きができる)を指定する。
    • io.MultiWriter関数を使用して以下の機能を追加する。
      • os.Stdout(画面に出力される内容)をlogfileに書き込むという設定を入れる。
    • log.SetFlags関数でログ出力時の設定を行う
      • ログの出力の日付(log.Ldate)
      • ログの出力の時間(log.Ltime)
      • ログの短いファイル名(log.Lshortfile)
      • ログのフルパスファイル名(log.Llongfile)

2.6 エラーハンドリングをしよう

プログラムにエラーが発生した時の処理をエラーハンドリングという。
発生したエラーの内容を表示させて原因を分かりやすくさせるなど。エラーハンドリングは重要な意味を持つため
Goにおけるエラーハンドリングについて確認しておく。

2.6.1 Goでエラーハンドリングをしよう

エラーが発生したとき、どのように処理(エラーハンドリング)すれば良いかを説明する。
例として、コードを書いたファイルをos.Openで読み込みます。この時、読み込んだファイルの内容を変数file、エラーの内容をerrに入れる。
エラーがある場合(変数errがnilでない場合)は、log.Fatallnで「Error!」と表示する。

func main() {
	file, err := os.Open("./lesson.go")
	if err != nil {
		log.Fatalln("Error!")
	}
	defer file.Close()
	data := make([]byte, 100)
	count, err := file.Read(data)
	if err != nil {
		log.Fatalln("Error!")
	}
	fmt.Println(count, string(data))
}
  • 複数の変数に代入する際のShort declarations
    • 変数のShort declarationsは、最低1つ初期化できるものがあれば使える。2回目のエラーハンドリングの際には、すでに宣言されているerrがある
    • もう一つの変数であるcountが初期化される変数のため、Short declarationsはエラーにならない
    • 2回目のエラーハンドリングでは変数errは上書きされるため、気を付けること

2.7 panicとrecover

panicとrecoverは、プログラムの強制終了を扱う際に使う関数です。
実際にプログラムでpanicとrecoverを書くことは推奨されていない。

2.7.1 panicでプログラムを強制終了させよう

panicは実行中のプログラムを停止させる仕組みの関数です。

func thirdPartyConnectDB() {
	panic("Unable to connect to DB")
}

func save() {
	thirdPartyConnectDB()
}

func main() {
	save()
	fmt.Println("OK?")
}

2.7.2 発生したpanicをrecoverで処理しよう

panicで発生したエラーを処理するには、recoverを使う。

func thirdPartyConnectDB() {
	panic("Unable to connect to DB")
}

func save() {
	defer func() {
		s := recover()
		fmt.Println(s)
	}()
	thirdPartyConnectDB()
}

func main() {
	save()
	fmt.Println("OK?")
}
⚠️ **GitHub.com Fallback** ⚠️