One place for hosting & domains

      January 2020

      Написание условных выражений в Go


      Введение

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

      Когда мы полностью исполняем каждое выражение программы, мы не просим, чтобы программа оценивала определенные условия. С условными выражениями программы могут определять выполнение определенных условий и получать соответствующие указания.

      Рассмотрим несколько примеров, когда стоит использовать условные выражения:

      • Если ученица получает за тест 65% или больше, она сдает его; если нет — не сдает.
      • Если у человека есть деньги на счете, рассчитывается процент, а если нет — начисляется штраф.
      • Если покупатель приобретает 10 апельсинов или более, он получает скидку 5%; если же он покупает меньше, скидка не предоставляется.

      Оценка условий и назначение кода для запуска в зависимости от выполнения условий — это основа использования условных выражений.

      В этом обучающем руководстве мы научимся писать условные выражения на языке программирования Go.

      Оператор If

      Начнем с выражений if, которые оценивают истинность утверждения и запускают код, только если оно истинно.

      Откройте в текстовом редакторе файл и введите следующий код:

      grade.go

      package main
      
      import "fmt"
      
      func main() {
          grade := 70
      
          if grade >= 65 {
              fmt.Println("Passing grade")
          }
      }
      

      В этом коде мы используем переменную grade и присваиваем ей значение 70. Затем мы используем оператор if для проверки того, что значения переменной grade больше или равно ( >= ) числу 65. Если это условие выполняется, мы указываем программе распечатать строку Passing grade.

      Сохраните программу с именем grade.go и запустите ее в локальной среде программирования из окна терминала с помощью команды go run grade.go.

      В данном случае оценка 70 соответствует условию (больше или равно 65), и после запуска программы вы получите следующий результат:

      Output

      Passing grade

      Теперь изменим результат программы, сменив значение переменной grade на 60:

      grade.go

      package main
      
      import "fmt"
      
      func main() {
          grade := 60
      
          if grade >= 65 {
              fmt.Println("Passing grade")
          }
      }
      

      Когда мы сохраим и запустим этот код, мы не получим никаких результатов, поскольку условие не выполнено, и мы не указали программе выполнить другое выражение.

      Рассмотрим еще один пример, где мы будем рассчитывать, является ли баланс счета отрицательным. Создайте файл account.go и напишите следующую программу:

      account.go

      package main
      
      import "fmt"
      
      func main() {
          balance := -5
      
          if balance < 0 {
              fmt.Println("Balance is below 0, add funds now or you will be charged a penalty.")
          }
      }
      

      При запуске этой программы с помощью комады go run account.go мы получим следующий результат:

      Output

      Balance is below 0, add funds now or you will be charged a penalty.

      В программе мы инициализировали переменную balance со значением -5, и это значение меньше 0. Поскольку баланс соответствует условию выражения if (balance < 0), при сохранении и запуске этого кода будет выведена строка. Если мы изменим баланс на 0 или положительное число, ничего выводиться не будет.

      Оператор Else

      Часто нам может понадобиться, чтобы программа что-то делала, даже когда оператор if равен false. В примере с оценкой мы хотим увидеть, сдан ли зачет.

      Для этого мы добавим к условию выражение else следующим образом:

      grade.go

      package main
      
      import "fmt"
      
      func main() {
          grade := 60
      
          if grade >= 65 {
              fmt.Println("Passing grade")
          } else {
              fmt.Println("Failing grade")
          }
      }
      

      Поскольку переменная grade имеет значение 60, выражение if оценивается как ложное, и программа не распечатывает текст Passing grade. Однако следующее выражение else предписывает программе выполнить другое действие.

      Когда мы сохраним и запустим эту программу, мы увидим следующее:

      Output

      Failing grade

      Если мы перепишем программу и присвоим переменной значение 65 или выше, вместо этого будет выводиться строка Passing grade.

      Чтобы добавить выражение else в пример с банковским счетом, мы перепишем код следующим образом:

      account.go

      package main
      
      import "fmt"
      
      func main() {
          balance := 522
      
          if balance < 0 {
              fmt.Println("Balance is below 0, add funds now or you will be charged a penalty.")
          } else {
              fmt.Println("Your balance is 0 or above.")
          }
      }
      

      Output

      Your balance is 0 or above.

      Здесь мы изменили значение переменной balance на положительное число, чтобы выполнить вывод в операторе else. Чтобы выполнить вывод первого оператора if, мы можем заменить значение отрицательным числом.

      Сочетая выражение if с выражением else, вы составляете условное выражение из двух частей, которое требует от компьютера выполнять определенный код если условие if выполнено, и если оно не выполнено.

      Операторы Else if

      Мы использовали для условных выражений опцию Boolean, с которой каждое выражение if оценивается или как истинное, или как ложное. Во многих случаях нам может потребоваться программа, которая будет оценивать более двух возможных результатов. Для этой цели мы используем выражение else if, которое имеет в Go синтаксис else if. Выражение else if выглядит похожим на выражение if и оценивает другое условие.

      В программе банковского счета нам может потребоваться три варианта вывода для трех разных случаев:

      • Баланс меньше 0
      • Баланс равен 0
      • Баланс больше 0

      Выражение else if помещается между выражением if и выражением else следующим образом:

      account.go

      package main
      
      import "fmt"
      
      func main() {
          balance := 522
      
          if balance < 0 {
              fmt.Println("Balance is below 0, add funds now or you will be charged a penalty.")
          } else if balance == 0 {
              fmt.Println("Balance is equal to 0, add funds soon.")
          } else {
              fmt.Println("Your balance is 0 or above.")
          }
      }
      

      Теперь при запуске программы возможны три результата:

      • Если переменная balance равняется 0, будет выведен результат выражения else if (Balance is equal to 0, add funds soon.)
      • Если переменная balance равняется положительному числу, будет выведен результат выражения else (Your balance is 0 or above.).
      • Если переменная balance равняется отрицательному числу, будет выведен результат выражения if (Balance is below 0, add funds now or you will be charged a penalty).

      Что делать, если нам требуется более трех вероятностей? В этом случае мы можем вставить в код несколько выражений else if.

      В программе grade.go мы перепишем код так, чтобы числовым оценкам соответствовали разные буквенные оценки:

      • 90 или выше соответствует оценке A
      • 80-89 соответствует оценке B
      • 70-79 соответствует оценке C
      • 65-69 соответствует оценке D
      • 64 или менее соответствует оценке F

      Для этого кода нам потребуется одно выражение if, три выражения else if и выражение else для всех случаев несоответствия.

      Перепишем код из предыдущего примера так, чтобы в строках распечатывались буквенные оценки. Мы можем сохранить выражение else без изменений.

      grade.go

      package main
      
      import "fmt"
      
      func main() {
          grade := 60
      
          if grade >= 90 {
              fmt.Println("A grade")
          } else if grade >= 80 {
              fmt.Println("B grade")
          } else if grade >= 70 {
              fmt.Println("C grade")
          } else if grade >= 65 {
              fmt.Println("D grade")
          } else {
              fmt.Println("Failing grade")
          }
      }
      

      Поскольку выражения else if будут проверяться по порядку, система выражений может быть очень простой. Данная программа выполняет следующие шаги:

      1. Если оценка больше 90, программа выводит текст A grade, если оценка меньше 90, программа переходит к следующему выражению…

      2. Если оценка больше или равна 80, программа выводит текст B grade, если оценка равняется 79 или менее, программа переходит к следующему выражению…

      3. Если оценка больше или равна 70, программа выводит текст С grade, если оценка равняется 69 или менее, программа переходит к следующему выражению…

      4. Если оценка больше или равна 65, программа выводит текст D grade, если оценка равняется 64 или менее, программа переходит к следующему выражению…

      5. Программа выводит текст Failing grade, поскольку ни одно из предыдущих условий не выполнено.

      Вложенные операторы If

      Вы познакомились с выражениями if, else if и else и теперь можете переходить к вложенным условным выражениям. Вложенные выражения if используются в ситуациях, когда нам нужно проверить дополнительное условие, если первое условие определяется как истинное. Для этого мы можем использовать выражение if-else внутри другого выражения if-else. Рассмотрим синтаксис вложенного выражения if:

      if statement1 { // outer if statement
          fmt.Println("true")
      
          if nested_statement { // nested if statement
              fmt.Println("yes")
          } else { // nested else statement
              fmt.Println("no")
          }
      
      } else { // outer else statement
          fmt.Println("false")
      }
      

      Этот код имеет несколько вариантов выводимых результатов:

      • Если выражение statement1 оценивается как истинное, программа проверяет истинность выражения nested_statement. Если оба выражения истинные, результат будет выглядеть следующим образом:

      Output

      true yes
      • Если выражение statement1 истинное, а выражение nested_statement — ложное, результат будет выглядеть следующим образом:

      Output

      true no
      • Если выражение statement1 ложное, вложенное выражение if-else не проверяется, и выполняется только выражение else. Результат будет выглядеть следующим образом:

      Output

      false

      Мы можем использовать в коде несколько вложенных выражений if:

      if statement1 { // outer if
          fmt.Println("hello world")
      
          if nested_statement1 { // first nested if
              fmt.Println("yes")
      
          } else if nested_statement2 { // first nested else if
              fmt.Println("maybe")
      
          } else { // first nested else
              fmt.Println("no")
          }
      
      } else if statement2 { // outer else if
          fmt.Println("hello galaxy")
      
          if nested_statement3 { // second nested if
              fmt.Println("yes")
          } else if nested_statement4 { // second nested else if
              fmt.Println("maybe")
          } else { // second nested else
              fmt.Println("no")
          }
      
      } else { // outer else
          statement("hello universe")
      }
      

      В этом коде вложенное выражение if используется внутри каждого выражения if в дополнение к выражению else if. Это дает больше вариантов для каждого условия.

      Рассмотрим пример вложенных выражений if в нашей программе grade.go. Вначале мы проверяем, соответствует ли оценка прохождению теста (больше или равняется 65%), а затем определяем буквенную оценку, которая соответствует числовой. Если оценка не соответствует прохождению, нам не нужно проверять буквенные значения, и программа просто сообщает, что тест не пройден. Наш измененный код с вложенным выражением if будет выглядеть следующим образом:

      grade.go

      
      package main
      
      import "fmt"
      
      func main() {
          grade := 92
          if grade >= 65 {
              fmt.Print("Passing grade of: ")
      
              if grade >= 90 {
                  fmt.Println("A")
      
              } else if grade >= 80 {
                  fmt.Println("B")
      
              } else if grade >= 70 {
                  fmt.Println("C")
      
              } else if grade >= 65 {
                  fmt.Println("D")
              }
      
          } else {
              fmt.Println("Failing grade")
          }
      }
      

      Если мы запустим код с переменной grade с целочисленным значением 92, первое условие выполняется, и программа выводит текст Passing grade of: . Затем программа видит, что оценка больше или равна 90, и поскольку это условие также выполнено, она выводит A.

      Если мы запустим код с переменной grade со значением 60, первое условие не будет выполняться, и программа пропустит вложенные выражения if и перейдет к выражению else, в результате чего будет выведен текст Failing grade.

      Мы можем добавить еще больше вариантов и использовать второй уровень вложенных выражений if. Например, мы можем захотеть использовать отдельные оценки A+, A и A-. Для этого вначале проверяем, что тест пройден, затем проверяем, что оценка равняется 90 или более, а затем проверим, что оценка больше 96 и соответствует значению A+:

      grade.go

      ...
      if grade >= 65 {
          fmt.Print("Passing grade of: ")
      
          if grade >= 90 {
              if grade > 96 {
                  fmt.Println("A+")
      
              } else if grade > 93 && grade <= 96 {
                  fmt.Println("A")
      
              } else {
                  fmt.Println("A-")
              }
      ...
      

      В этом коде, если для переменной grade задано значение 96, программа выполняет следующие действия:

      1. Проверяет, что оценка больше или равна 65 (истина)
      2. Выводит текст Passing grade of:
      3. Проверяет, что оценка больше или равна 90 (истина)
      4. Проверяет, что оценка больше 96 (ложь)
      5. Проверяет, что оценка больше 93 и меньше или равна 96 (истина)
      6. Выводит A
      7. Покидает вложенные условные выражения и продолжает выполнение остальной части кода

      Таким образом, для оценки 96 результат выполнения программы будет выглядеть так:

      Output

      Passing grade of: A

      Вложенные выражения if позволяют добавлять в код несколько определенных уровней условий.

      Заключение

      Использование условных выражений, в том числе выражения if, вы можете лучше контролировать выполняемый программой код. Условные выражения предписывают программе оценить выполнение определенных условий. Если условие выполнено, запускается определенный код, а если оно не выполнено — программа переходит к другому коду.

      Чтобы потренироваться в работе с условными выражениями и лучше познакомиться с ними, попробуйте использовать разные операторы.



      Source link

      Написание пакетов в Go


      Пакет состоит из файлов Go, которые находятся в том же каталоге и имеют одинаковое выражение пакета в начале. Вы можете добавить дополнительные функции из пакетов, чтобы усложнить программы. Некоторые пакеты доступны в стандартной библиотеке Go и устанавливаются вместе с Go. Другие можно установить с помощью команды go get в Go. Также вы можете создавать собственные пакеты Go, создавая файлы Go в том же каталоге, где вы хотите разместить общий код, используя необходимое выражение пакета.

      В этом обучающем руководстве вы научитесь писать пакеты Go для использования в других программных файлах.

      Предварительные требования

      • Настройте среду программирования Go в соответствии с указаниями одного из обучающих руководств серии Установка и настройка локальной среды программирования Go. Создайте рабочее пространство Go, следуя указаниям шага 5 обучающих руководств по локальной среде программирования. Чтобы следовать примерам и правилам присвоения имен из этой статьи, прочитайте первый раздел, посвященный написанию и импорту пакетов.
      • Чтобы узнать больше о GOPATH, прочитайте нашу статью Знакомство с GOPATH.

      Написание и импорт пакетов

      Пакеты пишутся так же, как любые другие файлы Go. Пакеты могут содержать определения функций, типов и переменных, которые могут использоваться в других программах Go.

      Перед созданием нового пакета необходимо перейти в рабочее пространство Go. Обычно его можно найти по адресу gopath. Например, в этом обучающем модуле мы назовем пакет greet. Для этого мы создали каталог с именем greet с путем gopath в рабочем пространстве проекта. Допустим наша организация называется gopherguides, и мы хотим создать пакет greet в каталоге организации, используя Github в качестве хранилища кода. В этом случае наш каталог будет выглядеть следующим образом:

      └── $GOPATH
          └── src
              └── github.com
                  └── gopherguides
      

      Каталог greet находится внутри каталога gopherguides:

      └── $GOPATH
          └── src
              └── github.com
                  └── gopherguides
                      └── greet
      

      Наконец, мы можем добавлять в каталог первый файл. Обычно первичному файлу или файлу точки входа в пакете присваивается имя каталога. В этом случае мы создадим файл с именем greet.go в каталоге greet:

      └── $GOPATH
          └── src
              └── github.com
                  └── gopherguides
                      └── greet
                          └── greet.go
      

      После создания файла мы можем начать писать код для повторного использования или совместного использования в нескольких проектах. В данном случае мы создадим функцию с именем Hello, которая выводит текст Hello World.

      Откройте файл greet.go в текстовом редакторе и добавьте следующий код:

      greet.go

      package greet
      
      import "fmt"
      
      func Hello() {
          fmt.Println("Hello, World!")
      }
      

      Разделим первый файл на части. В первой строке каждого файла нужно указать имя пакета, с которым вы работаете. Поскольку вы находитесь в пакете greet, вы используете ключевое слово package, за которым идет имя пакета:

      package greet
      

      Так компилятор будет рассматривать все содержание файла как часть пакета greet.

      Затем вы декларируете все другие пакеты, которые вам нужно использовать, с помощью выражения import. В этом файле вы используете только пакет fmt:

      import "fmt"
      

      В заключение вы создадите функцию Hello. Она будет использовать пакет fmt для вывода текста Hello, World!:

      func Hello() {
          fmt.Println("Hello, World!")
      }
      

      Теперь вы записали пакет greet и можете использовать его в любом другом создаваемом вами пакете. Создадим новый пакет, в котором будет использоваться ваш пакет greet.

      Вы создадите пакет с именем example, и это означает, что вам требуется каталог с именем example. Создайте пакет в организации gopherguides, чтобы структура каталогов выглядела следующим образом:

      └── $GOPATH
          └── src
              └── github.com
                  └── gopherguides
                          └── example
      

      Теперь у вас есть каталог нового пакета, и вы можете создать файл точки входа. Поскольку это исполняемая программа, следует присвоить файлу точки входа имя main.go:

      └── $GOPATH
          └── src
              └── github.com
                  └── gopherguides
                      └── example
                          └── main.go
      

      Откройте в текстовом редакторе файл main.go и добавьте следующий код для вызова пакета greet:

      main.go

      package main
      
      import "github.com/gopherguides/greet"
      
      func main() {
          greet.Hello()
      }
      

      Поскольку вы импортируете пакет, вам нужно вызвать функцию, сославшись на имя пакета в точечной нотации. Точечная нотация — это практика, предусматривающая постановку точки . между именем используемого пакета и ресурсом этого пакета, который вы хотите использовать. Например, в пакете greet вы можете использовать функцию Hello в качестве ресурса. Если вы хотите вызвать этот ресурс, вы можете использовать точечную нотацию greet.Hello().

      Теперь вы можете открыть терминал и запустить команду из командной строки:

      В этом случае вы получите следующий результат:

      Output

      Hello, World!

      Чтобы посмотреть, как использовать переменные в пакете, добавим определение переменной в файл greet.go:

      greet.go

      package greet
      
      import "fmt"
      
      var Shark = "Sammy"
      
      func Hello() {
          fmt.Println("Hello, World!")
      }
      

      Откройте файл main.go и добавьте следующую выделенную строку для вызова переменной из greet.go в функции fmt.Println():

      main.go

      package main
      
      import (
          "fmt"
      
          "github.com/gopherguides/greet"
      )
      
      func main() {
          greet.Hello()
      
          fmt.Println(greet.Shark)
      }
      

      После повторного запуска программы:

      Вывод должен выглядеть следующим образом:

      Output

      Hello, World! Sammy

      В заключение определим тип в файле greet.go. Вы создадите тип Octopus с полями name и color, а также функцию, которая будет выводить поля при вызове:

      greet.go

      package greet
      
      import "fmt"
      
      var Shark = "Sammy"
      
      type Octopus struct {
          Name  string
          Color string
      }
      
      func (o Octopus) String() string {
          return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
      }
      
      func Hello() {
          fmt.Println("Hello, World!")
      }
      

      Откройте файл main.go для создания экземпляра этого типа в конце файла:

      main.go

      package main
      
      import (
          "fmt"
      
          "github.com/gopherguides/greet"
      )
      
      func main() {
          greet.Hello()
      
          fmt.Println(greet.Shark)
      
          oct := greet.Octopus{
              Name:  "Jesse",
              Color: "orange",
          }
      
          fmt.Println(oct.String())
      }
      

      После создания экземпляра типа Octopus с помощью выражения oct := greet.Octopus вы сможете получить доступ к функциям и полям типа в пространстве имен файла main.go. Это позволит вам записать oct.String() в последнюю строку без вызова greet. Например, вы можете вызвать одно из полей типа, например oct.Color, не ссылаясь на имя пакета greet.

      Метод String типа Octopus использует функцию fmt.Sprintf для создания предложения и возвращает результат в виде строки вызывающему компоненту (в данном случае, основной программе).

      При запуске программы вы получите следующий результат:

      Output

      Hello, World! Sammy The octopus's name is "Jesse" and is the color orange.

      Создавая метод String в типе Octopus, вы получаете возможность многократно выводить информацию о своем пользовательском типе. Если вы захотите изменить поведение этого метода, вам нужно будет только отредактировать этот метод.

      Экспортированный код

      Возможно вы заметили, что все декларации в вызванном вами файле greet.go были указаны заглавными буквами. В Go отсутствует концепция модификаторов public, private и protected, как в других языках. Видимость для внешних элементов определяется использованием заглавных букв. Типы, переменные, функции и другие элементы, которые начинаются с заглавной буквы, находятся в публичной зоне видимости. Символ, видимый вне пакета, считается экспортируемым.

      Если вы добавите в Octopus новый метод с именем reset, вы можете вызвать его из пакета greet, но не из файла main.go, который находится вне пакета greet:

      greet.go

      package greet
      
      import "fmt"
      
      var Shark = "Sammy"
      
      type Octopus struct {
          Name  string
          Color string
      }
      
      func (o Octopus) String() string {
          return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
      }
      
      func (o *Octopus) reset() {
          o.Name = ""
          o.Color = ""
      }
      
      func Hello() {
          fmt.Println("Hello, World!")
      }
      
      

      Если вы попробуете вызвать метод reset из файла main.go:

      main.go

      package main
      
      import (
          "fmt"
      
          "github.com/gopherguides/greet"
      )
      
      func main() {
          greet.Hello()
      
          fmt.Println(greet.Shark)
      
          oct := greet.Octopus{
              Name:  "Jesse",
              Color: "orange",
          }
      
          fmt.Println(oct.String())
      
          oct.reset()
      }
      

      Вы получите следующую ошибку компиляции:

      Output

      oct.reset undefined (cannot refer to unexported field or method greet.Octopus.reset)

      Чтобы выполнить экспорт функции reset из Octopus, нужно сделать заглавной букву R в имени метода reset:

      greet.go

      package greet
      
      import "fmt"
      
      var Shark = "Sammy"
      
      type Octopus struct {
          Name  string
          Color string
      }
      
      func (o Octopus) String() string {
          return fmt.Sprintf("The octopus's name is %q and is the color %s.", o.Name, o.Color)
      }
      
      func (o *Octopus) Reset() {
          o.Name = ""
          o.Color = ""
      }
      
      func Hello() {
          fmt.Println("Hello, World!")
      }
      

      В результате вы можете вызвать метод Reset из другого пакета без сообщения об ошибке:

      main.go

      package main
      
      import (
          "fmt"
      
          "github.com/gopherguides/greet"
      )
      
      func main() {
          greet.Hello()
      
          fmt.Println(greet.Shark)
      
          oct := greet.Octopus{
              Name:  "Jesse",
              Color: "orange",
          }
      
          fmt.Println(oct.String())
      
          oct.Reset()
      
          fmt.Println(oct.String())
      }
      

      Теперь, если вы запустите программу:

      Результат будет выглядеть следующим образом:

      Output

      Hello, World! Sammy The octopus's name is "Jesse" and is the color orange The octopus's name is "" and is the color .

      Вызывая метод Reset, вы очистили всю информацию в полях Name и Color. При вызове метода String он не будет выводить никакого текста, где появляются Name и Color, поскольку теперь эти поля пустые.

      Заключение

      Написание пакета Go аналогично написанию любого другого файла Go, однако его размещение в другом каталоге позволяет изолировать код для повторного использования в любом месте. В этом обучающем руководстве мы показали, как написать определения пакета, продемонстрировали использование этих определений в другом файле Go и описали варианты хранения пакета для доступа к нему.



      Source link

      Использование оператора switch в Go


      Введение

      Условные выражения дают программистам возможность предписывать программам производить определенные действия, если условие выполняется, и другое действие, если условие не выполняется. Нам часто нужно сравнивать определенную переменную с разными возможными значениями и выполнять разные действия для разных результатов. Это можно реализовать, используя только операторы if. Однако написание программного обеспечения предусматривает необходимость не только делать так, чтобы все работало, но и показывать свои намерения себе и другим разработчикам. switch — это альтернативный условный оператор, с помощью которого удобно показывать действия программ Go в ситуациях с разными возможными вариантами.

      Все, что можно написать с помощью оператора switch, можно написать и с помощью операторов if. В этом обучающем руководстве мы рассмотрим несколько примеров того, что может делать оператор switch, как оно может заменить операторы if, и в каких случаях его лучше применять.

      Структура оператора switch

      Оператор 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")
          }
      }
      

      Результат будет выглядеть следующим образом:

      Output

      chocolate is great! vanilla is great! strawberry is my favorite! I've never tried banana before

      В main мы определяем срез вкусов мороженого. Затем мы используем цикл for для итерации. Мы используем три оператора if для вывода разных сообщений, показывающих разные предпочитаемые вкусы мороженого. Каждый оператор if должен использовать оператор continue для остановки выполнения цикла for, чтобы последнее сообщение по умолчанию не выводилось для предпочитаемых вкусов мороженого.

      При добавлении новых вкусов мороженого нам нужно добавлять операторы if для каждого нового случая. Для дублирующихся сообщений, как в случае с "vanilla" и "chocolate", следует использовать дублирующиеся операторы if. Тем, кто будет читать наш код в будущем (в том числе и нам), будет сложнее понять код из-за большого количества повторяющихся операторов if, что затрудняет понимание их функции — сравнение переменной с разными значениями и выполнение разных действий. Кроме того, общее сообщение задается отдельно от условий и выглядит не связанным с ними. Оператор switch поможет нам лучше организовать эту логику.

      Оператор switch начинается с ключевого слова switch, за которым идет переменная в базовой форме, для которой производится сравнение. Далее идет пара фигурных скобок ({}), в которых могут быть заключены разные варианты. Варианты описывают, какие действия должна выполнять программа Go, если переданная оператору switch переменная имеет значение, указанное в данном варианте. В следующем примере предыдущий пример конвертирован с использованием оператора 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")
              }
          }
      }
      

      Результат выглядит так же:

      Output

      chocolate is great! vanilla is great! strawberry is my favorite! I've never tried banana before

      Мы снова определяем срез вкусов мороженого в программе main и используем выражение range для итерации вкусов. Однако в данном случае мы используем выражение switch, которое оценивает переменную flav. Мы используем два выбирающих предложения для указания предпочтений. Нам больше не требуются операторы continue, поскольку только один вариант выполняется оператором switch. Также мы сможем комбинировать дублирующуюся логику условий "chocolate" и "vanilla", разделив их запятой в объявлении варианта. Вариант default используется как универсальный для всех случаев. Оно выполняется для всех вкусов, которые не включены в тело выражения switch. В данном случае для варианта "banana" будет использоваться программа default и будет выведено сообщение I've never tried banana before.

      Упрощенная форма оператора switch используется в самом распространенном случае: при сравнении переменной с несколькими альтернативными значениями. Также она обеспечивает дополнительное удобство, если мы хотим выполнять одно и то же действие для нескольких разных значений и другое действие, если не выполняется ни одно из заданных условий. Для этого используется ключевое слово default.

      Если упрощенная форма оператора switch слишком узкая, мы можем использовать более общую форму оператора 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
          }
      }
      

      Результат зависит от случайного числа и от того, как хорошо вы играете в игру. Вот пример результатов одного игрового сеанса:

      Output

      Enter 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 была возможность задать эту переменную. После обработки всех ошибок парсинга мы используем два оператора if для сравнения догадки пользователя со значением target. Возвращаемая строка вместе с булевым значением определяют, какое сообщение будет выведено игроку, и будет ли закрыта игра.

      Эти операторы 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
              }
          }
      }
      

      Результат выглядит примерно следующим образом:

      Output

      Enter a guess: 25 Too low! Enter a guess: 28 Too high! Enter a guess: 27 You win!

      В этой версии игры на отгадывание мы заменили блок операторов if на оператора switch. Мы пропускаем аргумент выражения для switch, поскольку мы используем switch только для объединения условий. Каждое выбирающее предложение содержит отдельное выражение сравнения guess и target. Как и в первом случае, когда мы заменили операторы if на оператор switch, нам больше не нужно использовать выражения continue, поскольку выполняется только одно выбирающее предложение. Наконец, выражение default отвечает за случай, когда guess == target, поскольку все остальные возможные варианты рассмотрены в двух других выбирающих предложениях.

      В приведенных примерах выполнялось только одно выбирающее выражение. Иногда нужно объединить варианты поведения нескольких выбирающих предложений. Оператор switch имеет еще одно ключевое слово для этой цели.

      Fallthrough

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

      Результат будет выглядеть примерно так:

      Output

      chocolate is great! vanilla is great! strawberry is my favorite! strawberry is great! I've never tried banana before

      Как мы уже видели, мы определяем срез строк для представления вкусов и используем цикл for для итерации. Здесь оператор switch повторяет использованный ранее, но в нем используется ключевое слово fallthrough в конце выбирающего предложения для варианта "strawberry". Так Go запускает тело case "strawberry": и вначале выводит строку strawberry is my favorite!. При появлении fallthrough выполняется тело следующего выбирающего предложения. При этом будет выполнено тело выражения case "vanilla", "chocolate": и будет выведен текст strawberry is great!.

      Ключевое слово fallthrough нечасто используется разработчиками на Go. Обычно повторное использование кода, реализуемое через ключевое слово fallthrough, лучше обеспечить посредством определения функции в общем коде. По этой причине использовать fallthrough не рекомендуется.

      Заключение

      Оператор switch помогает нам передать другим разработчикам, читающим наш код, что сравниваемые значения в наборе как-то связаны друг с другом. Это упрощает добавление других вариантов поведения и позволяет правильно обрабатывать все варианты, которые мы могли забыть благодаря использованию default. Когда вы в следующей раз будете писать код с несколькими оператора if для одной и той же переменной, попробуйте переписать его с помощью switch. Так вам будет проще переделать код, если в будущем потребуется добавить другие альтернативные значения.

      Если вы хотите узнать больше о языке программирования Go, ознакомьтесь с нашей серией статей о программировании на языке Go.



      Source link