Управление потоком

Мы уверенно движемся вперёд. Можно только позавидовать - впереди у нас много нового, неизведанного и удивительного. Любознательность, в определённой степени бескорыстная - в ней сущность нашего продвижения по ступенькам науки. Приступим!

Под управлением потоком мы будем понимать операторы ветвления и циклы, которые мы сейчас и разберём.

Начинать будем с отличий. Первое и основное, что нужно понимать по данной теме в языке Go - это отсутствие начисто таких конструкций, как while, do while, foreach. Весь ассортимент сведен к одному единственному оператору - for. Во-вторых, следует запомнить, что, в отличие от большинства С-подобных языков, в языке Go параметры цикла for и условия операторов ветвления никогда не берутся в круглые скобки! И, наконец, нужно не забывать семантическое значение переноса строки, фактически, замещающее собою применение точки с запятой (semicolon). Именно поэтому, возможна только нижеприведенная форма записи:

оператор условие {
}

И будут всегда ошибочны любые другие формы:

//Такая:
оператор условие
{
}
//И вот такая:
оператор условие {}

Оператор for

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

Прежде всего, оператор for может принимать классическую форму:

package main

import "fmt"

func main() {

	//круглые скобки не требуются!
	for i := 0; i <= 10; i++ { //фигурная скобка в той же строке!
		fmt.Println(i)
	}

}
//Результат: вывод чисел от 0 до 10

Пожалуй, оператор for - один из немногих случаев массового применения точки с запятой (semicolon), тщательно избегаемой в языке Go.

Объявление переменной-счётчика можно при необходимости вынести за пределы конструкции, тем самым расширив её зону видимости. Не забудьте только сохранить точку с запятой в положенном ей месте.

А вот так for может мимикрировать под цикл while:

Обратите внимание, точки с запятой в данной ипостаси цикла for можно опустить, но их наличие не будет ошибкой.

И, наконец, бесконечный цикл:

В данной конструкции точки с запятой также можно опустить, либо же использовать по желанию, чего я делать не рекомендую. Меньше знаков - чище код. Обратите внимание, нам пришлось использовать оператор break для выхода из бесконечного цикла. В случае использования оператора for в форме бесконечного цикла, это - единственная возможность из такого цикла выйти. При использовании подпрограмм Go (горутин), то есть многопоточности, зачастую используют бесконечные циклы, работа которых заканчивается только тогда, когда программа завершает свою работу. Рядом с оператором break стоит оператор continue, который прерывает лишь текущую итерацию цикла. При этом оператор for переходит к следующей итерации, не прерывая своей работы.

Выведем только чётные числа:

Операторы break и continue могут использоваться с метками, подобно оператору goto. Опишем синтаксис подобного явления. Для оживления изложения, построим так называемое "треугольное число" (последовательность A000217arrow-up-right в OEISarrow-up-right):

Циклы могут быть вложенными, к примеру при работе с многомерными массивами или при решении специфических задач.

Выведем, к примеру, таблицу Пифагора:

Обратите внимание - \t - как и во множестве C-подобных языков - символ табуляции. Подобные символы, а также символы форматирования, так называемые "verbs" мы рассмотрим позднее в разделе, посвящённом работе с текстом.

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

Здесь range - ключевое слово. Имена же переменных key и value даны для наглядности и могут быть заменены любыми. Далее, для понимания происходящего, мы используем массив. Знаю, что не проходили. Да, обещаю вскоре исправиться.

Key можно опустить:

Такова форма, если нужно опустить value:

Не станем спорить о пользе бесконечного цикла по диапазону; выглядит он таким образом:

Операторы break и continue в цикле по диапазону остаются в прежней силе. Пожалуй, это исчерпывающая информация о цикле for.

Оператор goto

Коль скоро с оператором for мы расстались на ноте break и continue, перейдём по горячему следу к оператору goto. Как и в языке C, используется данный оператор совместно с метками. Синтаксис нехитрый:

Существует тривиальное утверждение, что использование оператора goto - дурной тон программирования. Автор вполне согласен с данным утверждением. Это приём, ведущий к плохой читаемости кода и неочевидности ветвлений управления потоком, замысла программиста. Если вы использовали данный оператор - остановитесь, размыслите. Чаще всего, вы сможете таким образом "перефразировать" ваш код, что позволит в конечном счёте избавиться от goto. Однако, это довольно "прилипчивый" оператор, и иногда цена избавления от него - также неудобочитаемый и неясный код. To be or not to be - решение остаётся за вами. Приведу здесь вполне правдоподобный пример использования оператора goto. Пускай это будет программа "ping". Используя пакет стороннего разработчика github.com/sparrc/go-ping, программа производит опрос хоста ipAddr. В случае неудачи, программа повторяет это действие трижды.

Для оживления изложения, добавлю сюда, пожалуй, пример решения задачки из leetcode. Задание таково:

Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string "".

Example 1:

Input: strs = ["flower","flow","flight"]

Output: "fl"

Example 2:

Input: strs = ["dog","racecar","car"]

Output: "" Explanation: There is no common prefix among the input strings.

Решение:

Решение достаточно оригинальное, и к тому же демонстрирует высокое сравнительное быстродействие.

If...else - неразлучная пара

Если излагать предмет в том порядке, как это обычно делают во всех пособиях, то именно с операторов if и else следовало начинать повествование в этой главе. Однако применение этих операторов в языке Go достаточно тривиально, что и натолкнуло автора на мысль начать рассказ именно с наиболее интересного - с ярких отличий. Однако, по ходу изложения, мы уже столь часто были вынуждены прибегнуть к использованию if...else, что разобрать данный предмет пришло, кажется, самое время.

Итак, приведём пример обычного использования оператора if:

Как обычно, оператор if дополняет комплиметнарный ему оператор else:

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

Как и во многих других языках, в Go допустима конструкция else if:

Блоков else if может быть в коде несколько.

Особым приёмом, отличительным для языка Go, является объявление переменных и использование выражений присваивания внутри оператора if:

Обратите внимание на использование точки с запятой внутри такого выражения. Данная форма записи - очень экономная; она компактна, а также ограничивает область видимости и время существования объявленной таким образом переменной границами текущего блока кода. В уместном случае, рекомендуется пользоваться данным приёмом.

Необходимо сказать несколько слов об идеологии использования в языке Go операторов if...else. Повторим, что сердцевина этого языка - компактность и краткость. Именно поэтому, хорошим тоном программирования на Go считается так компоновать свой код, чтобы максимально сокращать все выражения, использовать как можно меньшее количество ветвлений, а в идеале - единственный оператор if.

Хорошо:

А так лучше:

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

Оператор switch

Особенностью оператора switch в языке Go, отличающей его, к примеру, от таких языков, как C/C++, C# и Java, является отсутствие так называемого "проваливания" от одного оператора case к другому. Закономерным следствием этого есть отсутствие необходимости использования оператора break в каждом кейсе. Как обычно, допускается и приветствуется использование заключительного default.

Пример обычного switch:

Чтобы сработал default:

Приведём более сложный пример (аргументом case является предикат):

Аргументом switch также может быть выражение:

После оператора case можно указывать несколько значений, разделённых запятыми:

Таким образом, мы рассмотрели все аспекты применения оператора switch.

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

Раунд!

Last updated