Управление потоком с помощью операторов 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
следует быть осторожным. Поскольку поведение, создаваемое этим кодом, может быть нежелательным.