GoでSwitchステートメントを書く方法

提供:Dev Guides
移動先:案内検索

序章

条件付きステートメントを使用すると、プログラマーは、条件が真の場合は何らかのアクションを実行し、条件が偽の場合は別のアクションを実行するようにプログラムに指示できます。 多くの場合、いくつかの変数を複数の可能な値と比較し、状況ごとに異なるアクションを実行します。 ifステートメントのみを使用してこの作業を行うことができます。 ただし、ソフトウェアを作成することは、物事を機能させるだけでなく、将来の自分や他の開発者にあなたの意図を伝えることでもあります。 switchは、さまざまなオプションが提示されたときにGoプログラムによって実行されるアクションを伝達するのに役立つ代替の条件ステートメントです。

switchステートメントで記述できるものはすべて、ifステートメントでも記述できます。 このチュートリアルでは、switchステートメントで実行できること、置き換えられるifステートメント、および最も適切に適用される場所の例をいくつか見ていきます。

Switchステートメントの構造

スイッチは通常、変数に特定の値が割り当てられたときにプログラムによって実行されるアクションを説明するために使用されます。 次の例は、ifステートメントを使用してこれを実現する方法を示しています。

package main

import "fmt"

func main() {
    flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}

    for _, flav := range flavors {
        if flav == "strawberry" {
            fmt.Println(flav, "is my favorite!")
            continue
        }

        if flav == "vanilla" {
            fmt.Println(flav, "is great!")
            continue
        }

        if flav == "chocolate" {
            fmt.Println(flav, "is great!")
            continue
        }

        fmt.Println("I've never tried", flav, "before")
    }
}

これにより、次の出力が生成されます。

Outputchocolate is great!
vanilla is great!
strawberry is my favorite!
I've never tried banana before

main内で、アイスクリームフレーバーのスライスを定義します。 次に、forループを使用してそれらを反復処理します。 3つのifステートメントを使用して、さまざまなアイスクリームフレーバーの好みを示すさまざまなメッセージを出力します。 各ifステートメントはcontinueステートメントを使用して、forループの実行を停止し、最後のデフォルトメッセージが優先アイスクリームフレーバーに対して出力されないようにする必要があります。

新しいアイスクリームの設定を追加するときは、新しいケースを処理するためにifステートメントを追加し続ける必要があります。 "vanilla"および"chocolate"の場合のように、重複したメッセージには、重複したifステートメントが含まれている必要があります。 私たちのコードの将来の読者(私たち自身を含む)にとって、ifステートメントの反復的な性質は、変数を複数の値と比較し、異なるアクションを実行するという、彼らが行っていることの重要な部分を覆い隠します。 また、フォールバックメッセージは条件とは別に設定されているため、無関係に見えます。 switchステートメントは、このロジックをより適切に整理するのに役立ちます。

switchステートメントは、switchキーワードで始まり、最も基本的な形式で、比較を実行するための変数が続きます。 この後に、複数のcase句を表示できる1組の中括弧({})が続きます。 case句は、switchステートメントに提供された変数がcase句によって参照される値と等しい場合にGoプログラムが実行する必要のあるアクションを記述します。 次の例は、前の例を変換して、複数のifステートメントの代わりにswitchを使用します。

package main

import "fmt"

func main() {
    flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}

    for _, flav := range flavors {
        switch flav {
        case "strawberry":
            fmt.Println(flav, "is my favorite!")
        case "vanilla", "chocolate":
            fmt.Println(flav, "is great!")
        default:
            fmt.Println("I've never tried", flav, "before")
        }
    }
}

出力は以前と同じです。

Outputchocolate is great!
vanilla is great!
strawberry is my favorite!
I've never tried banana before

mainでアイスクリームフレーバーのスライスを再度定義し、rangeステートメントを使用して各フレーバーを繰り返しました。 ただし、今回は、flav変数を調べるswitchステートメントを使用しました。 2つのcase句を使用して、設定を示します。 switchステートメントによって実行されるcase句は1つだけなので、continueステートメントは不要になりました。 case句の宣言でそれぞれをコンマで区切ることにより、"chocolate"条件と"vanilla"条件の重複ロジックを組み合わせることができます。 default句は、キャッチオール句として機能します。 switchステートメントの本文で説明されていないすべてのフレーバーに対して実行されます。 この場合、"banana"により、defaultが実行され、メッセージI've never tried banana beforeが出力されます。

この簡略化された形式のswitchステートメントは、変数を複数の選択肢と比較するという、それらの最も一般的な使用法に対応しています。 また、提供されたdefaultキーワードを使用して、リストされた条件のいずれも満たされない場合に、複数の異なる値に対して同じアクションを実行したり、他のアクションを実行したりする場合にも便利です。

この簡略化された形式のswitchが制限しすぎることが判明した場合は、より一般的な形式のswitchステートメントを使用できます。

一般的なSwitchステートメント

switchステートメントは、より複雑な条件のコレクションをグループ化して、それらが何らかの形で関連していることを示すのに役立ちます。 これは、前の例のように特定の値ではなく、ある変数を値の範囲と比較するときに最も一般的に使用されます。 次の例では、ifステートメントを使用して推測ゲームを実装します。これは、switchステートメントの恩恵を受ける可能性があります。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    target := rand.Intn(100)

    for {
        var guess int
        fmt.Print("Enter a guess: ")
        _, err := fmt.Scanf("%d", &guess)
        if err != nil {
            fmt.Println("Invalid guess: err:", err)
            continue
        }

        if guess > target {
            fmt.Println("Too high!")
            continue
        }

        if guess < target {
            fmt.Println("Too low!")
            continue
        }

        fmt.Println("You win!")
        break
    }
}

出力は、選択したランダムな数字とゲームのプレイの程度によって異なります。 1つのサンプルセッションからの出力は次のとおりです。

OutputEnter a guess: 10
Too low!
Enter a guess: 15
Too low!
Enter a guess: 18
Too high!
Enter a guess: 17
You win!

私たちの推測ゲームは推測を比較するためにランダムな数を必要とするので、math/randパッケージのrand.Intn関数を使用します。 ゲームをプレイするたびにtargetの値が異なることを確認するために、rand.Seedを使用して、現在の時刻に基づいて乱数ジェネレーターをランダム化します。 引数100からrand.Intnは、0〜100の範囲の数値を示します。 次に、forループを使用して、プレーヤーからの推測の収集を開始します。

fmt.Scanf関数は、ユーザー入力を選択した変数に読み込む手段を提供します。 これは、ユーザーの入力を期待するタイプに変換するフォーマット文字列動詞を取ります。 ここでの%dは、intが必要であり、guess変数のアドレスを渡して、fmt.Scanfがその変数を設定できるようにすることを意味します。 解析エラーを処理した後、2つのifステートメントを使用して、ユーザーの推測をtarget値と比較します。 返されるstringは、boolとともに、プレーヤーに表示されるメッセージと、ゲームを終了するかどうかを制御します。

これらのifステートメントは、変数が比較されている値の範囲がすべて何らかの方法で関連しているという事実をあいまいにします。 また、範囲の一部を見逃したかどうかを一目で判断するのは難しい場合があります。 次の例では、前の例をリファクタリングして、代わりにswitchステートメントを使用します。

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    target := rand.Intn(100)

    for {
        var guess int
        fmt.Print("Enter a guess: ")
        _, err := fmt.Scanf("%d", &guess)
        if err != nil {
            fmt.Println("Invalid guess: err:", err)
            continue
        }

        switch {
        case guess > target:
            fmt.Println("Too high!")
        case guess < target:
            fmt.Println("Too low!")
        default:
            fmt.Println("You win!")
            return
        }
    }
}

これにより、次のような出力が生成されます。

OutputEnter a guess: 25
Too low!
Enter a guess: 28
Too high!
Enter a guess: 27
You win!

このバージョンの推測ゲームでは、ifステートメントのブロックをswitchステートメントに置き換えました。 switchを使用して条件をまとめて収集することにのみ関心があるため、switchの式の引数は省略します。 各case句には、guesstargetを比較する異なる式が含まれています。 初めてifステートメントをswitchに置き換えたときと同様に、case句が1つだけ実行されるため、continueステートメントは不要になりました。 最後に、default句は、他の2つのcase句で他のすべての可能な値をカバーしているため、guess == targetの場合を処理します。

これまで見てきた例では、1つのcaseステートメントが実行されます。 場合によっては、複数のcase句の動作を組み合わせたいことがあります。 switchステートメントは、この動作を実現するための別のキーワードを提供します。

フォールスルー

別のcase句に含まれているコードを再利用したい場合があります。 このような場合、fallthroughキーワードを使用して、リストされている次のcase句の本体を実行するようにGoに要求することができます。 この次の例では、以前のアイスクリームフレーバーの例を変更して、ストロベリーアイスクリームに対する私たちの熱意をより正確に反映しています。

package main

import "fmt"

func main() {
    flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}

    for _, flav := range flavors {
        switch flav {
        case "strawberry":
            fmt.Println(flav, "is my favorite!")
            fallthrough
        case "vanilla", "chocolate":
            fmt.Println(flav, "is great!")
        default:
            fmt.Println("I've never tried", flav, "before")
        }
    }
}

次の出力が表示されます。

Outputchocolate is great!
vanilla is great!
strawberry is my favorite!
strawberry is great!
I've never tried banana before

前に見たように、フレーバーを表すためにstringのスライスを定義し、forループを使用してこれを繰り返します。 ここでのswitchステートメントは、前に見たものと同じですが、[のcase句の最後にfallthroughキーワードが追加されています。 X162X]。 これにより、Goはcase "strawberry":の本体を実行し、最初に文字列strawberry is my favorite!を出力します。 fallthroughに遭遇すると、次のcase句の本体を実行します。 これにより、case "vanilla", "chocolate":の本体が実行され、strawberry is great!が印刷されます。

fallthroughキーワードは、Go開発者によって頻繁に使用されることはありません。 通常、fallthroughを使用して実現されるコードの再利用は、共通のコードで関数を定義することでより適切に取得できます。 これらの理由から、fallthroughの使用は一般的に推奨されていません。

結論

switchステートメントは、コードを読んでいる他の開発者に、一連の比較が何らかの形で相互に関連していることを伝えるのに役立ちます。 これらにより、将来新しいケースが追加されたときにさまざまな動作を簡単に追加でき、default句を使用して忘れたものを適切に処理できるようになります。 次回、すべて同じ変数を含む複数のifステートメントを記述していることに気付いた場合は、switchステートメントを使用して書き直してみてください。他の代替値。

Goプログラミング言語について詳しく知りたい場合は、Goシリーズのコーディング方法全体を確認してください。