Управление потоком с помощью операторов switch

Завершено

Подобно другим языками программирования, Go поддерживает операторы switch. Операторы switch используются, чтобы предотвратить связывание нескольких операторов if. Использование операторов switch позволяет избежать сложного процесса поддержки и чтения кода, который включает множество операторов if. Эти операторы также упрощают создание сложных условий. Обратите внимание на работу операторов 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 выходные данные всегда будут одинаковые. Это одно из ограничений службы.)

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 регистр обрабатывает любые неоцененные значения.

Использование нескольких выражений

Иногда одному и тому же оператору case могут соответствовать сразу несколько выражений. Если необходимо, чтобы оператор case в Go включал в себя несколько выражений, разделите их, используя запятые (,). Это позволит избежать дублирования кода.

В следующем примере кода показано, как добавить несколько выражений.

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 вы можете пропустить условие в операторе switch, выполнив те же действия, что и для оператора if. Этот шаблон похож на сравнение значения true, как если бы вы задавали принудительное безостановочное выполнение оператора switch.

Ниже приведен пример написания оператора 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")
    }
}

Программа всегда запускает этот тип оператора switch, так как условие всегда имеет значение true. Условный блок switch может быть проще в обслуживании, чем длинная цепочка операторов if и else if.

Прохождение логики к следующему варианту

В некоторых языках программирования ключевое слово break нужно писать в конце каждого оператора case. Но в Go, когда логика переходит к одному варианту, она выходит из блока switch, если ее остановить явным образом. Чтобы логика сразу переходила к следующему варианту, используйте ключевое слово 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), он соответствует первому варианту. Но num не превышает 100. А поскольку первый оператор case содержит ключевое слово fallthrough, логика немедленно переходит к следующему оператору case без проверки варианта. При использовании ключевого слова fallthrough следует быть осторожным. Поскольку поведение, создаваемое этим кодом, может быть нежелательным.