Вступление
Conditional statements дает программистам возможность направлять свои программы на выполнение определенных действий, если условие истинно, и других действий, если условие ложно. Часто мы хотим сравнить несколькоvariable с несколькими возможными значениями, предпринимая разные действия в каждом случае. Это можно сделать, используя толькоif
statements. Написание программного обеспечения, однако, заключается не только в том, чтобы заставить вещи работать, но и в том, чтобы сообщить о своем намерении будущему себе и другим разработчикам. switch
- альтернативный условный оператор, полезный для сообщения действий, предпринимаемых вашими программами Go, когда они представлены с различными параметрами.
Все, что мы можем написать с помощью оператора switch, также можно записать с помощью операторовif
. В этом руководстве мы рассмотрим несколько примеров того, что может делать оператор switch, операторыif
, которые он заменяет, и где его наиболее целесообразно применять.
Структура операторов переключателя
Переключатель обычно используется для описания действий, выполняемых программой, когда переменной назначаются определенные значения. В следующем примере показано, как это сделать с помощью операторов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
для их обхода. Мы используем три оператораif
, чтобы распечатать разные сообщения, указывающие на предпочтения разных вкусов мороженого. Каждый операторif
должен использовать операторcontinue
, чтобы остановить выполнение циклаfor
, чтобы сообщение по умолчанию в конце не печаталось для предпочтительных вкусов мороженого.
Поскольку мы добавляем новые настройки мороженого, мы должны продолжать добавлять операторыif
для обработки новых случаев. Дублированные сообщения, как в случае"vanilla"
и"chocolate"
, должны иметь повторяющиеся операторыif
. Для будущих читателей нашего кода (включая нас самих) повторяющийся характер операторовif
скрывает важную часть того, что они делают - сравнения переменной с несколькими значениями и выполнения различных действий. Кроме того, наше резервное сообщение отделено от условных, что делает его не связанным. Операторswitch
может помочь нам лучше организовать эту логику.
Операторswitch
начинается с ключевого словаswitch
и сопровождается в его самой простой форме некоторой переменной, с которой выполняется сравнение. За ним следует пара фигурных скобок ({}
), где может отображаться несколькоcase clauses. Предложения case описывают действия, которые ваша программа Go должна выполнять, когда переменная, предоставленная оператору switch, равна значению, указанному в предложении case. В следующем примере предыдущий пример преобразуется для использованияswitch
вместо нескольких операторовif
:
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
для перебора каждого вкуса. Однако на этот раз мы использовали операторswitch
, который исследует переменнуюflav
. Мы используем два предложенияcase
для обозначения предпочтений. Нам больше не нужны операторыcontinue
, так как только одно предложениеcase
будет выполнено операторомswitch
. Мы также можем комбинировать повторяющуюся логику условных выражений"chocolate"
и"vanilla"
, разделяя их запятыми в объявлении предложенияcase
. Предложениеdefault
служит нашим всеобъемлющим предложением. Он будет работать с любыми вариантами, которые мы не учли в теле оператораswitch
. В этом случае"banana"
вызовет выполнениеdefault
, напечатав сообщениеI've never tried banana before
.
Эта упрощенная форма операторовswitch
предназначена для их наиболее частого использования: сравнения переменной с несколькими альтернативами. Это также обеспечивает удобство, когда мы хотим выполнить одно и то же действие для нескольких разных значений и некоторые другие действия, когда ни одно из перечисленных условий не выполняется, используя предоставленное ключевое словоdefault
.
Когда эта упрощенная форма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
}
}
Вывод будет зависеть от выбранного случайного числа и от того, насколько хорошо вы играете в игру. Вот вывод из одного примера сессии:
OutputEnter a guess: 10
Too low!
Enter a guess: 15
Too low!
Enter a guess: 18
Too high!
Enter a guess: 17
You win!
В нашей игре в угадывание нужно случайное число для сравнения предположений, поэтому мы используем функциюrand.Intn
из пакетаmath/rand
. Чтобы гарантировать получение разных значений дляtarget
каждый раз, когда мы играем в игру, мы используемrand.Seed
для рандомизации генератора случайных чисел на основе текущего времени. Аргумент от100
доrand.Intn
даст нам число в диапазоне 0–100. Затем мы используем циклfor
, чтобы начать сбор предположений от игрока.
Функцияfmt.Scanf
дает нам возможность читать вводимые пользователем данные в выбранную нами переменную. Он принимает глагол форматной строки, который преобразует введенные пользователем данные в ожидаемый нами тип. %d
здесь означает, что мы ожидаемint
, и мы передаем адрес переменнойguess
, чтобыfmt.Scanf
мог установить эту переменную. Послеhandling any parsing errors мы затем используем два оператора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
содержит другое выражение, сравнивающееguess
сtarget
. Как и в первый раз, когда мы заменили операторыif
наswitch
, нам больше не нужны операторыcontinue
, поскольку будет выполнено только одно предложениеcase
. Наконец, предложениеdefault
обрабатывает случай, когдаguess == target
, поскольку мы охватили все другие возможные значения двумя другими предложениямиcase
.
В примерах, которые мы видели до сих пор, будет выполнен ровно один оператор case. Иногда вам может потребоваться объединить поведение нескольких предложенийcase
. Операторыswitch
предоставляют еще одно ключевое слово для достижения такого поведения.
Провалиться
Иногда вам может понадобиться повторно использовать код, содержащийся в другом предложенииcase
. В этих случаях можно попросить Go запустить тело следующего предложенияcase
, указанное с помощью ключевого словаfallthrough
. Этот следующий пример изменяет наш предыдущий пример вкуса мороженого, чтобы более точно отражать наш энтузиазм по поводу клубничного мороженого:
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
здесь идентично тому, которое мы видели ранее, но с добавлением ключевого словаfallthrough
в конце предложенияcase
для"strawberry"
. Это заставит 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 целиком.