Это вторая статья из моей еженедельной серии Learning Go. На прошлой неделе я обсуждал историю Go, его мыслительные основы, переменные и типы. На этой неделе я нырнул на довольно знакомую территорию. Многие концепции пришли ко мне быстро благодаря моему опыту работы с JavaScript; однако было действительно здорово покопаться в различиях в том, как эти концепции реализованы на другом языке. Приступим к делу.

Поток управления, что это?

порядок, в котором отдельные операторы, инструкции или вызовы функций императивной программы выполняются или оцениваются

Мне казалось, что я понял концепцию чего-то вроде потока управления еще до того, как узнал о его месте в информатике; однако понимание его значения в сочетании с структурами управления позволило мне гораздо яснее представить себе, как выполняется мой код.

По сути, это концепция, которую мы используем для определения того, как наш код будет интерпретироваться и выполняться. Поток управления разбит на три структуры управления:

  • Последовательный
  • Итеративный
  • Условный

Петли

последовательность инструкций, которые постоянно повторяются до тех пор, пока не будет выполнено заданное условие

Независимо от того, занимаетесь ли вы программированием 10 месяцев или 10 лет, скорее всего, вы довольно часто использовали циклы. Я не буду тратить много времени на механику работы циклов, но я хочу обратиться к их основам. Вы можете довольно просто разбить то, что я называю тремя столпами цикла. Эти три столпа представляют собой оператор инициализации, оператор условия и оператор публикации. Конечно, у нас есть код, который выполняется в теле цикла as, пока оператор условия равен true после итерации.

for init statement; condition statement; post statement {
    // code that is executed in each iteration of the loop
}

Важное примечание. В go нет циклов while.

ключевое слово "для"

указывает повторное выполнение блока кода

При использовании ключевого слова for вы создаете то, что называется оператором for. Существует три формы управления итерацией с помощью оператора for:

  • одно условие
  • оговорка «за»
  • оговорка «диапазон»

Одно условие

В одном операторе условия условие после ключевого слова for оценивается перед выполнением. Чтобы код выполнялся, условие должно оцениваться как true.

for 1 < 2 {
// run code
}

предложение «для»

Это то, что большинство считает традиционным циклом for, который они используют. При таком использовании ключевого слова for мы используем три столпа цикла: оператор инициализации, оператор условия и оператор post.

package main
import (
    "fmt"
)
func main() {
    for i := 0; i <= 3; i++ {
        fmt.Println(i)
    }
    // 0    
    // 1
    // 2
    // 3
}

предложение «диапазон»

Предложение диапазона используется для перебора всех записей среза, массива, строки, карты или значений, полученных из канала (мы углубимся в каналы в более поздней записи). Я продемонстрирую использование типа slice — позже мы углубимся в этот тип.

package main
import (
    "fmt"
)
func main() {
    s := []int{1, 2, 3, 4, 5}
    for i, v := range s {
        fmt.Println(i, v)
    }
    // 0      1
    // 1      2
    // 2      3
    // 3      4
    // 4      5
    // index  value
}

Что там происходит?

  • мы назначаем переменную s переменной slice, которая будет содержать значения типа int, эти значения 1, 2, 3, 4, 5
  • при использовании предложения range мы init получаем два значения: index и value — здесь i и v
  • на каждой итерации этого цикла i (позиция в массиве) будет увеличиваться на 1 , v будет отражать value в указанном index из slice

Важное примечание. Срезы и массивы имеют нулевую индексацию — это означает, что значение index всегда будет начинаться с 0 , а не с 1

Операторы перерыва

останавливает (завершает) выполнение самой внутренней инструкции for , switch или select

Мне нравится думать об операторах break как о спасательных люках для вашего кода. Если есть условие, при котором вы не хотите продолжать итерацию, оператор break — лучший способ остановить выполнение и перейти к следующему фрагменту исполняемого кода.

Краткий пример:

package main
import (
   "fmt"
)
func main() {
    n := 0
for {
        n++
        if n > 5 {
            break
        }
        fmt.Println(n)
    }
}

Позвольте мне рассказать вам о том, что здесь происходит:

  • внутри функции main объявляем переменную n и присваиваем ей значение 0
  • мы используем ключевое слово for для создания цикла
  • внутри цикла мы используем оператор ++ для увеличения n на 1
  • используя оператор if, мы оцениваем, больше ли значение n, чем 5
  • используя пакет fmt из пакета Standard Library от go, мы печатаем текущее значение n
  • мы повторяем 5 times до тех пор, пока значение n не станет больше 5 — затем мы используем ключевое слово break и выполнение завершается

Продолжить заявления

является следующей итерацией самого внутреннего цикла for в его пост-операторе

Ранее я упоминал, что в go нет цикла while — я обнаружил, что использование оператора continue внутри цикла for может привести к тем же результатам.

package main
import (
    "fmt"
)
func main() {
    n := 1
    for {
        z++
        if n > 10 {
            break
        }
        if n%2 != 0 {
            continue
        }
        fmt.Println(n)
        // 2
        // 4
        // 6
        // 8
        // 10
    }
}

Продолжить заявления

В приведенном выше примере я пытаюсь find all numbers evenly divisible by 2, позвольте мне рассказать вам, как я это делаю, используя операторы break и continue:

  • внутри функции main объявляем переменную n и присваиваем ей значение 1
  • мы используем ключевое слово for для создания цикла
  • внутри цикла мы используем оператор ++ для увеличения n на 1
  • используя оператор if, мы оцениваем, больше ли значение n, чем 10 , это оценивается как false для первых 10 итераций.
  • затем каждая итерация будет оценивать, является ли значение n не равномерным делится на 2, мы можем определить это с помощью оператора modulo
  • значения, которые меньше 10 и не делятся без остатка на 2, не входят ни в один оператор if
  • используя пакет fmt из пакета Standard Library от go, мы печатаем текущее значение n
  • примечание: будут напечатаны только числа, которые делятся на 2 без остатка —это потому, что их значения не дают true ни для одного оператора if

Условные операторы

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

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

Вот несколько примеров условных операторов:

  • Операторы if-then-else
  • Операторы «иначе если»

если еще

package main
import (
    "fmt"
)
func main() {
    x := 1
    if x == 2 {
        fmt.Println("equal")
    } else {
        fmt.Println("not equal")
    }
}

Достаточно прямой пример. Внутри функции main мы объявляем переменную со значением 1. Далее мы оцениваем, равно ли значение x 2. Это не так; поэтому мы переходим к оператору else. Оператор else по сути является оператором default, который выполняет код за время, которое оператор if оценивает как false.

Важное примечание: исходя из JavaScript, я привык использовать оператор === для оценки строгого сравнения типов и значений, как вы можете видеть в go, оператор выглядит вот так ==.

иначе, если

package main
import (
    "fmt"
)
func main() {
    x := 1
    if x == 2 {
        fmt.Println("equal to 2")
    } else if x == 3 {
        fmt.Println("equal to 3")
    } else {
        fmt.Println("not equal")
    }
}

Единственная разница здесь в том, что мы добавляем дополнительный branch, который может быть выполнен, если он оценивается как true. Вместо двух ветвей (как в последнем примере) у нас теперь три. Это позволяет вам добавить некоторые динамические аспекты к вашей функции.

Все операторы if должны начинаться с ветви if и должны иметь ветвь else, чтобы служить default; однако между этими ветвями вы можете добавить столько else if ветвей, сколько пожелаете. Хотя добавление нескольких не рекомендуется в большинстве случаев из-за удобочитаемости кода и потенциальной производительности.

Операторы переключения

обеспечивает многоходовое исполнение. выражение или спецификатор типа сравнивается с каждым case внутри оператора switch

  • операторы switch должны иметь регистр по умолчанию
  • если выражение для случая не найдено, его значение равно true
  • каждый case сравнивается со значением выражения switch
package main
import (
    "fmt"
)
func main() {
    switch {
    case false:
        fmt.Println("this will not print")
    case (2 == 4):
        fmt.Println("this is not true")
    case (4 == 5):
        fmt.Println("not true either")
    default:
        fmt.Println("default case")
    }
}

Выше вы можете видеть, что мы создаем оператор switch, который содержит три оператора case, и все они оцениваются как false; следовательно, выполняется случай default.

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

Здесь мы используем буквальное значение:

package main
import (
    "fmt"
)
func main() {
    switch "Yoda" {
    case "Obi Wan":
        fmt.Println("you don't need to see his identification")
    case "Darth Vader":
        fmt.Println("I am your father")
    default:
        fmt.Println("the chosen one, found I have not")
    }
}

Здесь мы используем переменную с одним регистром:

package main
import (
    "fmt"
)
func main() {
    y := "Yoda"
    switch y {
    case "Luke Skywalker":
        fmt.Println("your father he is")
    case "Quigon Jinn":
        fmt.Println("clouded this boys future is")
    default:
        fmt.Println("There is another skywalker")
    }
}

Здесь мы используем переменную с несколькими случаями:

package main
import (
    "fmt"
)
func main() {
    y := "Yoda"
    switch y {
    case "Darth Maul", "Palpatine", "Mace Windu":
        fmt.Println("wars make not one great")
    case "Quigon Jinn":
        fmt.Println("always two there are, no more no less")
    default:
        fmt.Println("when 900 years old you reach, look as good you will not")
    }
}

В итоге

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