Go基礎 #3 制御フロー — if, for, switch
#2 変数、型、定数 で値を扱う道具を見ました。今回は — その値で分岐と繰り返しを作る場面。
if / else if / else
#
条件分岐。他の言語とほぼ似ていますが、丸括弧なしが特徴です。
score := 85
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else {
fmt.Println("C")
}if (条件) ではなく if 条件 です。C/Javaスタイルから来ると最初は違和感がありますが、すぐ慣れます。
短い宣言 (short statement) #
条件の中で変数を一つ宣言してすぐ使うことができます。Goで非常によく出会うパターン。
if v, ok := lookup(key); ok {
fmt.Println("見つかった:", v)
} else {
fmt.Println("なし")
}v, ok := lookup(key) がifの中で起こり、v と ok はif/elseブロック内でのみ見えます。ブロックを抜けると消えます。
このパターンがGoコードのいたるところに登場します。エラー検査(#4)の標準的な形でもあります。
条件は bool のみ
#
x := 5
// if x { ... } ✗ x は int
if x > 0 { ... } // OK
// if v := lookup(); v { ... } ✗ v が bool でないとダメJavaScriptのtruthy/falsyのような自動変換がありません。明示的な条件のみ受け付けます。
for — 唯一の繰り返し文
#
Goには while がありません。for 一つですべての繰り返しを表現します。
1) 古典的なfor — 三部分 #
for i := 0; i < 5; i++ {
fmt.Println(i)
}
// 0, 1, 2, 3, 4C/Javaと同じ形。初期化 ; 条件 ; 各反復後。
2) whileのように — 条件のみ #
n := 0
for n < 5 {
fmt.Println(n)
n++
}三部分のうち条件だけがある形。他の言語のwhileと同じ意味です。
3) 無限ループ — 条件もなし #
for {
// ...
if shouldStop() {
break
}
}for { } が無限ループ。while (true) と同じ意味です。
4) for range — コレクション巡回
#
fruits := []string{"りんご", "バナナ", "ぶどう"}
for i, fruit := range fruits {
fmt.Println(i, fruit)
}
// 0 りんご
// 1 バナナ
// 2 ぶどうages := map[string]int{
"カーティス": 30,
"アリス": 25,
}
for name, age := range ages {
fmt.Println(name, age)
}for i, r := range "あいう" {
fmt.Printf("%d: %c\n", i, r)
}
// 0: あ
// 3: い (UTF-8 バイトインデックス)
// 6: うfor range は非常に強力です。インデックス/キーだけ必要なら2番目の変数を省略し、値だけ必要なら最初を _ で無視します。
for i := range fruits { // インデックスのみ
fmt.Println(i)
}
for _, fruit := range fruits { // 値のみ
fmt.Println(fruit)
}break と continue
#
ループの流れを制御。
for i := 0; i < 10; i++ {
if i == 5 {
break // ループから抜ける
}
if i%2 == 0 {
continue // 次の反復へ
}
fmt.Println(i)
}
// 1, 3ラベルで入れ子ループ制御 #
何重もの入れ子ループから一度に抜けたいとき。
outer:
for i := 0; i < 5; i++ {
for j := 0; j < 5; j++ {
if i*j > 6 {
break outer // outerラベルまで一気にbreak
}
}
}頻繁には使いませんが、深い入れ子では綺麗です。
switch — Goの最大の違い
#
Goのswitchは自動的にfallthroughしません。C/Javaと真逆です。
day := "mon"
switch day {
case "mon", "tue", "wed", "thu", "fri":
fmt.Println("平日")
case "sat", "sun":
fmt.Println("週末")
default:
fmt.Println("?")
}各caseの末尾に自動break。古いCのように break を書く必要がありません。複数の値を一つのcaseにカンマでまとめることもできます。
強制fallthrough #
本当に次のcaseへ流したければ fallthrough キーワード。
n := 1
switch n {
case 1:
fmt.Println("one")
fallthrough
case 2:
fmt.Println("two")
}
// one
// twoほとんど使いません。意図が明確なときだけ。
条件式switch — if/elseの短い形 #
式のないswitchはif/elseの連鎖を短くします。
score := 85
switch {
case score >= 90:
fmt.Println("A")
case score >= 80:
fmt.Println("B")
case score >= 70:
fmt.Println("C")
default:
fmt.Println("F")
}各caseがbool式。長くなるif/else ifより見やすいことが多いです。
switch + 短い宣言 #
ifと同様にswitchも短い宣言を許します。
switch v := getValue(); {
case v > 100:
fmt.Println("大きい")
case v > 0:
fmt.Println("正の数")
default:
fmt.Println("0 以下")
}型switch — interface分岐 #
中級 #1 インターフェース で本格的に扱いますが、switchが型検査にも使われます。
func describe(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("int:", v)
case string:
fmt.Println("string:", v)
case bool:
fmt.Println("bool:", v)
default:
fmt.Println("unknown")
}
}i.(type) という特別な構文。インターフェースがどの具体型を持っているかに応じて分岐します。
goto — ほとんど使わない
#
Goにも goto はありますが、ほとんど使いません。本当に特殊な場面(入れ子からの脱出、後始末コード)でだけ出会います。
短いifの罠 — 波括弧必須 #
JavaScriptと違い、Goは波括弧が常に必須です。
// if condition doSomething() ✗ コンパイルエラー
if condition {
doSomething()
}この設計のおかげで、JavaScriptの「インデントだけの偽if」のような罠がそもそもありません。
よく使うパターン #
1) エラー検査 #
if err != nil パターンがGoコードで最もよく見る形の一つです。
file, err := os.Open("data.txt")
if err != nil {
return err
}
defer file.Close()
// ... 正常処理#4 で詳しく扱います。
2) キーの存在確認 #
ages := map[string]int{"カーティス": 30}
if age, ok := ages["カーティス"]; ok {
fmt.Println(age)
}map[キー] は2つの値を返すことができます — 値と存在の有無 ok。この短い宣言パターンが標準です。
3) early return #
深いif/elseより早めにreturnするパターン。
func process(s string) error {
if s == "" {
return fmt.Errorf("空文字列")
}
if len(s) > 100 {
return fmt.Errorf("文字列が長すぎる")
}
// メインロジック — インデント1段だけ
return nil
}JavaScript #4 関数 で見たパターンと同じです。Goでも推奨されます。
まとめ #
今回の記事で整理した内容:
if 条件 { }— 丸括弧なし、波括弧必須- 短い宣言 —
if v, ok := ...; ok { }がよく登場 - 条件はboolのみ — truthy/falsy自動変換なし
forが唯一の繰り返し文 — while/do-whileなし- 4つの形 — 古典、条件のみ、無限、range
for rangeでスライス/マップ/文字列を巡回 (文字列はrune)switchは自動break — fallthroughは明示- 条件式switchでif/else連鎖を整理
i.(type)で型switch (プレビュー)
次の記事(#4 関数、多値返却、error型)ではGoの関数 — 定義方法、多値返却、そして最も重要なエラー処理パターンを扱います。