🦫 Самоучитель по Go для начинающих. Часть 5. Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
В этой части самоучителя мы узнаем про составные части любой программы – условные конструкции и циклы, рассмотрим особенности их реализации в Go, а также решим несколько занимательных задач для закрепления теоретического материала.
Условные конструкции
Условные конструкции предназначены для изменения работы программы в зависимости от заданных условий. Они позволяют управлять потоком выполнения, обрабатывать ошибки и реагировать на различные сценарии.
В Go есть два оператора для создания условных конструкций – if-else и switch-case. Познакомимся с каждым из них подробнее.
Оператор if-else
Оператор if-else
в Go имеет стандартный для многих языков программирования синтаксис:
С помощью оператора if-else
можно, к примеру, проверить число на чётность:
Эту же конструкцию можно записать в более компактном виде, используя короткое объявление оператора if
(if short statement):
Иногда в программе необходимо обработать несколько различных условий. В таких случаях на помощь приходит оператор else if
, который проверяет условие в случае ложности предыдущего. Продемонстрируем работу else if на примере программы для определения типа числа, введенного с клавиатуры:
Буквально конструкцию if-elseif-else можно воспринимать так:
Условия также могут содержать логические операторы, работа которых основана на законах алгебры логики. Для примера приведём код, проверяющий одновременно два чисел на чётность:
Также допускается создание вложенных условий. Но стоит помнить, что высокий уровень вложенности усложняет программу и затрудняет ее понимание.
Например, с помощью вложенных условий можно реализовать простейшую систему аутентификации пользователя:
В этом примере мы запрашиваем ввод логина и далее сравниваем введенное значение со строкой "Gosha"
. В случае соответствия логина выводим сообщение и ожидаем ввод пароля, иначе выводим "Неверный логин"
. После проверки пароля на равенство строке "ilovego"
печатаем сообщение либо об успешном входе, либо о неверном пароле.
Обработка ошибок
В Go оператор if
часто используется для обработки ошибок. Чтобы продемонстрировать это поведение, воспользуемся функцией strconv.Atoi
, которая возвращает два значения – результат перевода строки в число и ошибку.
Для проверки наличия ошибки используется оператор if
, который сравнивает значение переменной err
с нулевым (nil
). Если err != nil
, то это говорит о возникновении ошибки, которую нужно обработать в блоке оператора if
.
Switch-case
Оператор switch-case
выполняет те же задачи, что и if-else
, но позволяет записывать условные выражения в более коротком виде. Он состоит из четырех ключевых слов: switch
– для создания оператора, case
– для проверки условий (аналог if
), default
– для задания значения по умолчанию в случае ложности предыдущих условий (аналог else
), fallthrough
– для принудительного выполнения следующего блока case.
Синтаксис switch-case
выглядит следующим образом:
Пример применения switch-case
с указанием переменной для проверки:
Все операторы case выполняются последовательно. Но что если необходимо обязательно учесть определенное условие? В таком случае используется ключевое слово fallthrough
. Оно ставится в конце блока case и указывает на то, что следующий оператор case будет выполнен независимо от истинности своего условия.
Пример switch-case
без переменной для проверки и с ключевым словом fallthrough
:
В результате будет выведено:
Цикл for
Циклы наряду с условными конструкциями являются составными частями большинства программ и позволяют выполнять повторяющиеся операции заданное количество раз. Это бывает полезно при работе со структурами данных, например для перебора значений массива или ключей мапы.
Основным элементом цикла является итератор – переменная, которая по заданному правилу изменяет свое значение.
В отличие от других языков, Go для создания цикла имеет только один оператор for
. В общем случае его синтаксис выглядит так:
В качестве примера посчитаем квадраты чисел от 1 до 9 с использованием цикла:
Для достижения необходимого результата бывает полезно изменять параметры цикла. К примеру, отрицательный шаг позволит перебрать значения в обратном порядке, но в таком случае начальное значение должно быть больше конечного:
Шаг необязательно должен изменяться на единицу. Вместо этого можно использовать любое значение:
Как было упомянуто ранее, циклы часто используются для итерации по массивам. Это реализуется с использованием ключевого слова range, которое задает диапазон перебираемых значений:
Обратите внимание, что первой переменной в таком цикле является индекс, а второй – значение.
При объявлении цикла for
необязательно указывать все параметры. Такая особенность была введена неслучайно, ведь это делает возможным реализацию второго типа циклов – while
(цикл «пока»). Он выполняется до тех пор, пока заданное условие истинно.
Давайте перепишем предыдущий код для подсчета квадратов чисел от 1 до 9 в стиле цикла while
:
При работе с таким циклом стоит следить за выполнением условия выхода и правильно изменять итератор, иначе есть риск получить бесконечный цикл, способный привести к зависанию или полному краху программы:
Помимо проверки условия есть другой подход для выхода из цикла – оператор break
. Он принудительно завершает выполнение цикла. Приведенный ниже код печатает квадраты чисел от 1 до 4 включительно, при этом break
обеспечивает досрочный выход из for при достижении итератором числа 5:
Несмотря на кажущуюся непригодность, бесконечные циклы бывают полезны для обеспечения бесперебойной работы программы. Чтобы это проиллюстрировать, напишем код для непрерывного считывания данных с консоли до ввода определённого символа:
Но если есть оператор, останавливающий цикл, то должен быть и продолжающий! И он есть – это оператор continue
, досрочно переходящий к следующей итерации. Он бывает полезен для пропуска определенных значений. Например, код ниже выведет на экран квадраты всех чисел от 1 до 20, не делящихся на 3 или 7:
Вложенные циклы
Циклы, как и условные конструкции, могут быть вложенными. Это полезный инструмент для генерации комбинаторных объектов и обработки многомерных структур данных, таких как n-мерные массивы, деревья и графы.
Простым примером вложенного цикла является программа для вывода на экран таблицы умножения чисел от 1 до 10:
При работе с вложенными конструкциями всегда стоит помнить об их отрицательном влиянии на производительность и читаемость кода.
Чтобы проиллюстрировать влияние вложенных циклов на производительность, обратимся к примеру с тройным уровнем вложенности, где каждый цикл выполняется n раз, и посчитаем количество итераций:
В примере выше количество операций будет 1003, а в общем случае говорят, что такая программа имеет сложность «порядка n в кубе» или O(n3). На маленьких значениях разница во времени выполнения может быть несущественна, но уже для n = 1000000
программа будет исполнятся критически долго, что недопустимо в условиях реальной разработки. Поэтому стоит избегать необдуманного использования вложенных циклов и оптимизировать их с помощью эффективных алгоритмов.
Для знакомства с понятием сложности алгоритмов рекомендуется прочитать статью Асимптотическая сложность алгоритмов: что за зверь?
Задачи
Пришло время применить изученную теорию на практике. Предлагаем решить несколько несложных задач на условные конструкции и циклы.
Задача 1: Гоша учится считать
Первокласснику Гоше на уроке математики дали задание: посчитать сумму всех чисел от 20 до 500, которые делятся на 6, и при этом не делятся на 8. Помогите Гоше решить эту задачу.
Решение:
Задача 2: Гоша изобретает калькулятор
Гоша хочет облегчить себе жизнь и создать собственный калькулятор на языке Go, чтобы быстро производить базовые операции над числами.
Входные данные: в первой строке вводятся два целых числа, а на следующей – один из четырех возможных символов, обозначающих математическую операцию: +
, -
, *
, /
Выходные данные: результат применения операции к числам, в случае неверного ввода возвращается сообщение: ««Ошибка, введите два целых числа и одну из четырех допустимых операций: +, -, *, /»
Решение: используем условную конструкцию switch-case
Задача 3: Гоша учится играть в шахматы
Гоша решил сыграть партию в шахматы со своим одноклассником, но забыл, как ходит ладья. Помогите Гоше определить, может ли ладья за один ход попасть с первой клетки на вторую.
Входные данные: четыре числа от 1 до 8 – номер столбца и номер строки сначала для первой клетки, а потом для второй.
Выходные данные: "YES", если из первой клетки можно за один ход попасть во вторую, иначе "NO".
Решение: для проверки условия достаточно сравнить пары координат клеток по вертикали и горизонтали. Если хотя бы одна пара совпадает, то ладья сможет попасть из одной клетки в другую, иначе – нет.
Задача 4: Гоша считает делители
Гоше стало интересно, как можно найти сумму делителей заданного числа n. Давайте поможем ему решить эту задачу, написав алгоритм на Go.
Входные данные: целое число n.
Выходные данные: сумма делителей числа n.
Решение: достаточно в цикле проверить деление n на все числа от 1 до n / 2, не забыв учесть само число n.
Задача 5: Гоша решает задачу со звездочкой
Гоше поручили написать код для детской игры FizzBuzz, правила которой следующие: игроки называют числа подряд, если число делится на 3, его заменяют на «Fizz», если делится на 5, то заменяют на «Buzz», а если делится на 3 и на 5 одновременно, то заменяют на «FizzBuzz». Ваша задача вывести все числа от 1 до 200 по правилам этой игры.
Решение: сразу оговоримся, что у этой задачи есть несколько решений. Самое очевидное заключается в рассмотрении всех возможных случаев, для каждого из которых выводится соответствующая строка. В представленном здесь решении используется похожий подход: каждую итерацию цикла создается строка, к которой приписывается определенное значение, зависящее от делимости итератора на 3 и 5. Если он не делится ни на одно из этих чисел, то к строке припишется строковое значение итератора.
Подведём итоги
В этой части самоучителя по Go мы познакомились с фундаментальными понятиями в программировании – условными конструкциями и циклами. Их понимание позволит в дальнейшем создавать гибкие и функциональные программы.
В следующем уроке рассмотрим функции, аргументы, рекурсию и defer,
а в конце по традиции закрепим теорию интересными задачами.
Содержание самоучителя
- Особенности и сфера применения Go, установка, настройка
- Ресурсы для изучения Go с нуля
- Организация кода. Пакеты, импорты, модули. Ввод-вывод текста.
- Переменные. Типы данных и их преобразования. Основные операторы
- Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
- Функции и аргументы. Области видимости. Рекурсия. Defer
- Массивы и слайсы. Append и сopy. Пакет slices
- Строки, руны, байты. Пакет strings. Хеш-таблица (map)
- Структуры и методы. Интерфейсы. Указатели. Основы ООП
- Наследование, абстракция, полиморфизм, инкапсуляция
- Обработка ошибок. Паника. Восстановление. Логирование
- Обобщенное программирование. Дженерики
- Работа с датой и временем. Пакет time
- Интерфейсы ввода-вывода. Буферизация. Работа с файлами. Пакеты io, bufio, os
- Конкурентность. Горутины. Каналы
- Тестирование кода и его виды. Table-driven подход. Параллельные тесты
- Основы сетевого программирования. Стек TCP/IP. Сокеты. Пакет net