switch ステートメントを使用する制御フロー

完了

他のプログラミング言語と同様に、Go では switch ステートメントがサポートされています。 switch ステートメントは、複数の if ステートメントをつなげるのを避けるために使用します。 多くの if ステートメントが含まれるコードを保守したり読んだりするのは困難ですが、switch ステートメントを使用するとそれがなくなります。 また、これらのステートメントを使用すると、複雑な条件を簡単に作成できるようになります。 以下のセクションでは switch ステートメントを見ていきましょう。

switch の基本的な構文

if ステートメントと同様に、switch の条件にかっこは必要ありません。 switch ステートメントの最も単純な形式は次のようになります。

package main

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

func main() {
    sec := time.Now().Unix()
    rand.Seed(sec)
    i := rand.Int31n(10)

    switch i {
    case 0:
        fmt.Print("zero...")
    case 1:
        fmt.Print("one...")
    case 2:
        fmt.Print("two...")
    }

    fmt.Println("ok")
}

上記のコードを複数回実行すると、毎回異なる出力が表示されます。 (ただし、Go Playground でコードを実行すると、毎回同じ結果が得られます。これは、サービスの制限事項の 1 つです)

Go では、一致する条件が見つかるまで、switch ステートメントの各ケースが比較されます。 ただし、前のコードでは、num 変数で可能性のあるすべての値のケースがカバーされていないことに注意してください。 num が最終的に 5 になる場合、プログラムの出力は ok になります。

また、既定のユース ケースをより具体的に示し、次のように指定することもできます。

switch i {
case 0:
    fmt.Print("zero...")
case 1:
    fmt.Print("one...")
case 2:
    fmt.Print("two...")
default:
    fmt.Print("no match...")
}

default ケースの場合、検証式を記述しないことに注意してください。 i 変数の値の有効性が case ステートメントに照らして検証され、検証されない値が default ケースによって処理されます。

複数の式を使用する

複数の式がただ 1 つの case ステートメントと一致する場合があります。 Go では、case ステートメントに複数の式を含めたい場合は、コンマ (,) を使用して式を区切ります。 この手法により、コードの重複を防ぐことができます。

次のコード サンプルでは、複数の式を含める方法を示します。

package main

import "fmt"

func location(city string) (string, string) {
    var region string
    var continent string
    switch city {
    case "Delhi", "Hyderabad", "Mumbai", "Chennai", "Kochi":
        region, continent = "India", "Asia"
    case "Lafayette", "Louisville", "Boulder":
        region, continent = "Colorado", "USA"
    case "Irvine", "Los Angeles", "San Diego":
        region, continent = "California", "USA"
    default:
        region, continent = "Unknown", "Unknown"
    }
    return region, continent
}
func main() {
    region, continent := location("Irvine")
    fmt.Printf("John works in %s, %s\n", region, continent)
}

case ステートメントの式に含める値は、switch ステートメントで検証する変数のデータ型に対応していることに注意してください。 新しい case ステートメントとして整数値を指定した場合、プログラムはコンパイルされません。

関数の呼び出し

switch で関数を呼び出すこともできます。 その関数からの考えられる戻り値に対する case ステートメントを記述できます。 たとえば、次のコードでは、time.Now() 関数を呼び出しています。 表示される出力は、現在の曜日によって異なります。

package main

import (
    "fmt"
    "time"
)

func main() {
    switch time.Now().Weekday().String() {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("It's time to learn some Go.")
    default:
        fmt.Println("It's the weekend, time to rest!")
    }

    fmt.Println(time.Now().Weekday().String())
}

switch ステートメントから関数を呼び出すと、関数から返される内容が常に検証されるため、式を変更せずにそのロジックを変更できます。

また、case ステートメントから関数を呼び出すこともできます。 たとえば、正規表現を使用して特定のパターンに一致させるには、この方法を使用します。 次に例を示します。

package main

import "fmt"

import "regexp"

func main() {
    var email = regexp.MustCompile(`^[^@]+@[^@.]+\.[^@.]+`)
    var phone = regexp.MustCompile(`^[(]?[0-9][0-9][0-9][). \-]*[0-9][0-9][0-9][.\-]?[0-9][0-9][0-9][0-9]`)

    contact := "foo@bar.com"

    switch {
    case email.MatchString(contact):
        fmt.Println(contact, "is an email")
    case phone.MatchString(contact):
        fmt.Println(contact, "is a phone number")
    default:
        fmt.Println(contact, "is not recognized")
    }
}

switch ブロックに検証式がないことに注意してください。 その概念については、次のセクションで説明します。

条件を省略する

Go では、if ステートメントの場合と同様に、switch ステートメントで条件を省略できます。 このパターンは、switch ステートメントを常に実行するように、true 値と比較するのと似ています。

条件のない switch ステートメントを記述する方法の例を次に示します。

package main

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

func main() {
    rand.Seed(time.Now().Unix())
    r := rand.Float64()
    switch {
    case r > 0.1:
        fmt.Println("Common case, 90% of the time")
    default:
        fmt.Println("10% of the time")
    }
}

条件は常に true なので、この種の switch ステートメントはプログラムで常に実行されます。 条件付き switch ブロックの方が、if ステートメントと else if ステートメントの長いチェーンよりメンテナンスが簡単な場合があります。

ロジックが次の case にフォールスルーされるようにする

一部のプログラミング言語では、すべての case ステートメントの最後に break キーワードを記述します。 しかし Go の場合は、ロジックが 1 つの case に分岐すると、明示的に停止しない限り、switch ブロックは終了します。 ロジックがすぐ次の case にフォールスルーされるようにするには、fallthrough キーワードを使用します。

このパターンをもっとよく理解するには、次のコード サンプルを見てください。

package main

import (
    "fmt"
)

func main() {
    switch num := 15; {
    case num < 50:
        fmt.Printf("%d is less than 50\n", num)
        fallthrough
    case num > 100:
        fmt.Printf("%d is greater than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is less than 200", num)
    }
}

コードを実行し、出力を分析します。

15 is less than 50
15 is greater than 100
15 is less than 200

何が間違っているかわかりますか。

num は 15 (50 未満) なので、最初の case と一致します。 しかし、num は 100 より大きくはありません。 また、最初の case ステートメントには fallthrough キーワードがあるため、ロジックは case を検証せずに次の case ステートメントに直ちに移動します。 したがって、fallthrough キーワードを使用する場合は注意が必要です。 このコードによって望まない動作が作成される可能性があります。