GoでSwitchステートメントを記述する方法

前書き

Conditional statementsは、条件が真の場合は何らかのアクションを実行し、条件が偽の場合は別のアクションを実行するようにプログラムに指示する機能をプログラマーに提供します。 多くの場合、いくつかのvariableを複数の可能な値と比較し、状況ごとに異なるアクションを実行します。 if statementsのみを使用してこの作業を行うことができます。 ただし、ソフトウェアを書くことは、物事を機能させるだけでなく、自分の意思を将来の自分や他の開発者に伝えることでもあります。 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内で、アイスクリームフレーバーのsliceを定義します。 次に、for loopを使用してそれらを反復処理します。 3つのifステートメントを使用して、さまざまなアイスクリームフレーバーの好みを示すさまざまなメッセージを出力します。 各ifステートメントはcontinueステートメントを使用してforループの実行を停止し、最後のデフォルトメッセージが優先アイスクリームフレーバーに対して出力されないようにする必要があります。

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

switchステートメントは、switchキーワードで始まり、最も基本的な形式で、比較を実行するための変数が続きます。 この後に、複数のcase clausesが表示される可能性のある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ステートメントは、より複雑な条件のコレクションをグループ化して、それらが何らかの形で関連していることを示すのに役立ちます。 これは、前の例のように特定の値ではなく、変数を値の範囲と比較するときに最も一般的に使用されます。 次の例では、switchステートメントの恩恵を受ける可能性のあるifステートメントを使用して推測ゲームを実装しています。

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が必要であることを意味し、fmt.Scanfがその変数を設定できるように、guess変数のアドレスを渡します。 handling any parsing errorsの後、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に置き換えたときと同様に、1つのcase句のみが実行されるため、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ステートメントは、前に見たものと同じですが、"strawberry"case句の最後にfallthroughキーワードが追加されています。 これにより、Goはcase "strawberry":の本体を実行し、最初に文字列strawberry is my favorite!を出力します。 fallthroughに遭遇すると、次のcase句の本体を実行します。 これにより、case "vanilla", "chocolate":の本体が実行され、strawberry is great!が出力されます。

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

結論

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

Goプログラミング言語について詳しく知りたい場合は、How To Code in Go series全体を確認してください。