フリーキーズ | 独学プログラミング

Go言語の制御フロー!if/switch/for文の使い方を初心者にもわかりやすく解説

リンドくん

リンドくん

たなべ先生、Go言語を勉強し始めたんですけど、「制御フロー」って何ですか?なんだか難しそうで...

たなべ

たなべ

制御フローはプログラムの流れを制御する仕組みのことなんだ。
例えば「もし雨が降ったら傘を持つ」「1から10まで数える」といった、条件や繰り返しを表現する方法だよ。

プログラミングを学び始めると、必ず出会うのが「制御フロー」という概念です。
これは、プログラムがどのような順序で処理を実行するかを決める、極めて重要な仕組みです。

Go言語における制御フローの主役は、if文(条件分岐)、switch文(多分岐)、for文(繰り返し)の3つです。
これらを理解することで、より柔軟で実用的なプログラムを作成できるようになります。

本記事では、Go言語の制御フローを初心者の方でも理解できるよう、基本的な概念から実践的な使い方まで、段階的に解説していきます。

プログラミング学習でお悩みの方へ

HackATAは、エンジニアを目指す方のためのプログラミング学習コーチングサービスです。 経験豊富な現役エンジニアがあなたの学習をサポートします。

✓ 質問し放題

✓ β版公開中(2025年内の特別割引)

HackATAの詳細を見る

if文で条件分岐をマスターしよう

リンドくん

リンドくん

if文って他の言語でも使いますよね。Go言語では何か特別なことがあるんですか?

たなべ

たなべ

そうなんだ!Go言語のif文には独特の特徴があるんだよ。
例えば、条件式に括弧を付ける必要がないし、変数の初期化と条件判定を同時にできるんだ。

基本的なif文の構文

Go言語のif文は、他の言語と比べて非常にシンプルです。最大の特徴は、条件式を括弧で囲む必要がないことです。

package main

import "fmt"

func main() {
    score := 85
    
    // 基本的なif文
    if score >= 80 {
        fmt.Println("合格です!")
    }
    
    // if-else文
    age := 17
    if age >= 18 {
        fmt.Println("成人です")
    } else {
        fmt.Println("未成年です")
    }
    
    // if-else if-else文
    temperature := 25
    if temperature >= 30 {
        fmt.Println("暑いです")
    } else if temperature >= 20 {
        fmt.Println("快適です")
    } else {
        fmt.Println("寒いです")
    }
}

Go言語独特の初期化付きif文

Go言語の大きな特徴の一つが、if文の中で変数を初期化できることです。この変数のスコープはif文のブロック内に限定されるため、一時的な変数を使いたい場合に非常に便利です。

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	// 初期化付きif文
	if num, err := strconv.Atoi("123"); err == nil {
		fmt.Printf("変換成功: %d\n", num)
	} else {
		fmt.Printf("変換失敗: %v\n", err)
	}

	// numとerrはここでは使用できない(スコープ外)

	// 実用的な例:ファイルサイズのチェック
	filename := "example.txt"
	if fileInfo, err := os.Stat(filename); err == nil {
		fmt.Printf("ファイルサイズ: %d bytes\n", fileInfo.Size())
	} else {
		fmt.Printf("ファイルが見つかりません: %v\n", err)
	}
}

複数条件の組み合わせ

Go言語では、論理演算子を使って複数の条件を組み合わせることができます。

package main

import "fmt"

func main() {
    age := 25
    hasLicense := true
    
    // AND演算子(&&)
    if age >= 18 && hasLicense {
        fmt.Println("運転できます")
    }
    
    // OR演算子(||)
    weather := "雨"
    if weather == "雨" || weather == "雪" {
        fmt.Println("傘が必要です")
    }
    
    // NOT演算子(!)
    isWeekend := false
    if !isWeekend {
        fmt.Println("平日です")
    }
}

switch文で多分岐処理を効率化

リンドくん

リンドくん

if文がたくさん続くと読みにくくなりませんか?

たなべ

たなべ

その通り!そんな時に便利なのがswitch文なんだ。
Go言語のswitch文は他の言語より柔軟で、breakを書く必要がないのも特徴だよ。

基本的なswitch文

Go言語のswitch文は、複数の条件を効率的に処理できる制御構造です。他の言語とは異なり、各caseの最後にbreakを書く必要がありません

package main

import "fmt"

func main() {
    day := "火曜日"
    
    switch day {
    case "月曜日":
        fmt.Println("週の始まりです")
    case "火曜日", "水曜日", "木曜日":
        fmt.Println("平日です")
    case "金曜日":
        fmt.Println("TGIF!")
    case "土曜日", "日曜日":
        fmt.Println("週末です")
    default:
        fmt.Println("不明な曜日です")
    }
}

型によるswitch(type switch)

Go言語独特の機能として、型による分岐があります。これは、interface{}型の変数が実際にどの型を持っているかで分岐する仕組みです。

package main

import "fmt"

func describe(value interface{}) {
    switch v := value.(type) {
    case int:
        fmt.Printf("整数: %d\n", v)
    case string:
        fmt.Printf("文字列: %s\n", v)
    case bool:
        fmt.Printf("真偽値: %t\n", v)
    case []int:
        fmt.Printf("整数スライス: %v\n", v)
    default:
        fmt.Printf("不明な型: %T\n", v)
    }
}

func main() {
    describe(42)
    describe("Hello")
    describe(true)
    describe([]int{1, 2, 3})
    describe(3.14)
}

条件式付きswitch

switch文では、値だけでなく条件式での分岐も可能です。この場合、switchの後に変数を書かず、各caseで条件を指定します。

package main

import "fmt"

func main() {
    score := 85
    
    switch {
    case score >= 90:
        fmt.Println("優秀")
    case score >= 80:
        fmt.Println("良好")
    case score >= 70:
        fmt.Println("普通")
    case score >= 60:
        fmt.Println("合格")
    default:
        fmt.Println("不合格")
    }
    
    // 時間帯による挨拶の例
    hour := 14
    switch {
    case hour < 12:
        fmt.Println("おはようございます")
    case hour < 18:
        fmt.Println("こんにちは")
    default:
        fmt.Println("こんばんは")
    }
}

for文で繰り返し処理をマスター

リンドくん

リンドくん

Go言語にはwhile文がないって聞いたんですけど、本当ですか?

たなべ

たなべ

そうなんだ!Go言語ではfor文だけで全ての繰り返し処理を表現するんだ。
シンプルでわかりやすいのが特徴だよ。

基本的なfor文の3つの形

Go言語のfor文には、主に3つの書き方があります。

1. 伝統的なfor文(カウンタ付き)

package main

import "fmt"

func main() {
    // 1から10まで出力
    for i := 1; i <= 10; i++ {
        fmt.Printf("%d ", i)
    }
    fmt.Println()
    
    // 逆順で出力
    for i := 10; i >= 1; i-- {
        fmt.Printf("%d ", i)
    }
    fmt.Println()
    
    // 2つずつ増加
    for i := 0; i <= 20; i += 2 {
        fmt.Printf("%d ", i)
    }
    fmt.Println()
}

2. while文的な使い方

package main

import "fmt"

func main() {
    // while文のような動作
    count := 0
    for count < 5 {
        fmt.Printf("カウント: %d\n", count)
        count++
    }
    
    // 無限ループ(条件を省略)
    counter := 0
    for {
        if counter >= 3 {
            break // ループを抜ける
        }
        fmt.Printf("無限ループ内: %d\n", counter)
        counter++
    }
}

3. range付きfor文(コレクション用)

package main

import "fmt"

func main() {
    // スライスの要素を順番に処理
    fruits := []string{"りんご", "バナナ", "オレンジ"}
    
    // インデックスと値の両方を取得
    for i, fruit := range fruits {
        fmt.Printf("%d: %s\n", i, fruit)
    }
    
    // 値のみを取得(インデックスは不要)
    for _, fruit := range fruits {
        fmt.Printf("好きな果物: %s\n", fruit)
    }
    
    // インデックスのみを取得
    for i := range fruits {
        fmt.Printf("インデックス: %d\n", i)
    }
    
    // マップの処理
    scores := map[string]int{
        "田中": 85,
        "佐藤": 92,
        "山田": 78,
    }
    
    for name, score := range scores {
        fmt.Printf("%s: %d点\n", name, score)
    }
}

ループ制御 break、continue、ラベル

Go言語では、breakcontinueを使ってループの流れを制御できます。また、ラベルを使って複数のネストしたループを一度に抜けることも可能です。

package main

import "fmt"

func main() {
    // continue: 現在の反復をスキップ
    for i := 1; i <= 10; i++ {
        if i%2 == 0 {
            continue // 偶数はスキップ
        }
        fmt.Printf("奇数: %d\n", i)
    }
    
    // break: ループを終了
    for i := 1; i <= 10; i++ {
        if i > 5 {
            break // 5より大きくなったら終了
        }
        fmt.Printf("数値: %d\n", i)
    }
    
    // ラベル付きbreak(ネストしたループ用)
Outer:
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if i*j > 4 {
                break Outer // 外側のループまで一気に抜ける
            }
            fmt.Printf("%d × %d = %d\n", i, j, i*j)
        }
    }
}

Go言語らしい制御フローのベストプラクティス

エラーハンドリングとの組み合わせ

Go言語では、エラーハンドリングと制御フローを組み合わせることが非常に重要です。

package main

import (
    "fmt"
    "strconv"
)

func processNumbers(inputs []string) {
    for i, input := range inputs {
        // 初期化付きif文でエラーハンドリング
        if num, err := strconv.Atoi(input); err != nil {
            fmt.Printf("入力%d「%s」は無効です: %v\n", i+1, input, err)
            continue // 次の入力へ
        } else {
            // 正常な場合の処理
            switch {
            case num < 0:
                fmt.Printf("負の数: %d\n", num)
            case num == 0:
                fmt.Printf("ゼロです\n")
            case num <= 100:
                fmt.Printf("1-100の範囲: %d\n", num)
            default:
                fmt.Printf("大きな数: %d\n", num)
            }
        }
    }
}

func main() {
    inputs := []string{"42", "abc", "-5", "0", "150"}
    processNumbers(inputs)
}

早期リターンパターン

Go言語では、早期リターンを使ってネストを浅くするのが推奨されています。

package main

import (
    "fmt"
    "errors"
)

func validateUser(name string, age int, email string) error {
    // 悪い例: ネストが深い
    /*
    if name != "" {
        if age >= 0 {
            if email != "" {
                return nil
            } else {
                return errors.New("メールアドレスが空です")
            }
        } else {
            return errors.New("年齢が不正です")
        }
    } else {
        return errors.New("名前が空です")
    }
    */
    
    // 良い例: 早期リターン
    if name == "" {
        return errors.New("名前が空です")
    }
    
    if age < 0 {
        return errors.New("年齢が不正です")
    }
    
    if email == "" {
        return errors.New("メールアドレスが空です")
    }
    
    return nil
}

func main() {
    users := [][3]interface{}{
        {"田中太郎", 25, "tanaka@example.com"},
        {"", 30, "test@example.com"},
        {"佐藤花子", -5, "sato@example.com"},
        {"山田次郎", 40, ""},
    }
    
    for i, user := range users {
        name := user[0].(string)
        age := user[1].(int)
        email := user[2].(string)
        
        if err := validateUser(name, age, email); err != nil {
            fmt.Printf("ユーザー%d: バリデーションエラー - %v\n", i+1, err)
        } else {
            fmt.Printf("ユーザー%d: %s (年齢: %d) - OK\n", i+1, name, age)
        }
    }
}

まとめ

リンドくん

リンドくん

Go言語の制御フローって、他の言語より使いやすそうですね!

たなべ

たなべ

そうだね!Go言語の制御フローはシンプルで強力なんだ。
特に初期化付きif文や、breakが不要なswitch文は、コードをより読みやすくしてくれるよ。

本記事では、Go言語の制御フロー(if文、switch文、for文)について詳しく解説しました。重要なポイントを振り返ってみましょう。

Go言語の制御フローの特徴

  • if文 → 括弧不要で、初期化付きif文により変数のスコープを限定できる
  • switch文 → break不要で、型スイッチや条件式スイッチも可能
  • for文 → while文やdo-while文の代わりとしても使え、rangeで簡潔にコレクションを処理できる

これらの機能を活用することで、読みやすく保守しやすいコードを書くことができます。
特に、エラーハンドリングとの組み合わせや早期リターンパターンは、Go言語らしいコーディングスタイルの基盤となります。

プログラミング学習において制御フローの理解は必須ですが、Go言語のシンプルで一貫性のある文法により、初心者の方でも比較的習得しやすいのではないでしょうか。
ぜひ今回紹介したサンプルコードを実際に動かして、Go言語の制御フローに慣れ親しんでください。

この記事をシェア

関連するコンテンツ