Хочешь уверенно проходить IT-интервью?

Мы понимаем, как сложно подготовиться: стресс, алгоритмы, вопросы, от которых голова идёт кругом. Но с AI тренажёром всё гораздо проще.
💡 Почему Т1 тренажёр — это мастхэв?
- Получишь настоящую обратную связь: где затык, что подтянуть и как стать лучше
- Научишься не только решать задачи, но и объяснять своё решение так, чтобы интервьюер сказал: "Вау!".
- Освоишь все этапы собеседования, от вопросов по алгоритмам до диалога о твоих целях.
Зачем листать миллион туториалов? Просто зайди в Т1 тренажёр, потренируйся и уверенно удиви интервьюеров. Мы не обещаем лёгкой прогулки, но обещаем, что будешь готов!
Реклама. ООО «Смарт Гико», ИНН 7743264341. Erid 2VtzqwP8vqy
Условные конструкции
Условные конструкции предназначены для изменения работы программы в зависимости от заданных условий. Они позволяют управлять потоком выполнения, обрабатывать ошибки и реагировать на различные сценарии.
В Go есть два оператора для создания условных конструкций – if-else и switch-case. Познакомимся с каждым из них подробнее.
Оператор if-else

Оператор if-else
в Go имеет стандартный для многих языков программирования синтаксис:
if условие {
// блок кода в случае истинности условия
} else {
// блок кода в случае ложности условия
}
С помощью оператора if-else
можно, к примеру, проверить число на чётность:
num := 12
if num % 2 == 0 {
fmt.Println("Число чётное")
} else {
fmt.Println("Число нечётное")
}
Эту же конструкцию можно записать в более компактном виде, используя короткое объявление оператора if
(if short statement):
if num := 12; num % 2 == 0 {
fmt.Println("Число чётное")
} else {
fmt.Println("Число нечётное")
}
Иногда в программе необходимо обработать несколько различных условий. В таких случаях на помощь приходит оператор else if
, который проверяет условие в случае ложности предыдущего. Продемонстрируем работу else if на примере программы для определения типа числа, введенного с клавиатуры:
package main
import (
"fmt"
)
func main() {
var num int
fmt.Scan(&num) // ввод числа
if num > 0 {
fmt.Println("Число положительное")
} else if num < 0 {
fmt.Println("Число отрицательное")
} else {
fmt.Println("Число равно нулю")
}
}
Буквально конструкцию if-elseif-else можно воспринимать так:
если условие1 {
блок кода в случае истинности условия1
} иначе если условие2 {
блок кода в случае истинности условия2
} иначе {
блок кода в случае ложности всех предыдущих условий
}
Условия также могут содержать логические операторы, работа которых основана на законах алгебры логики. Для примера приведём код, проверяющий одновременно два чисел на чётность:
var a, b = 1, 2
if a%2 == 0 && b%2 == 0 {
fmt.Println("Оба числа чётные")
} else if a%2 == 0 || b%2 == 0 {
fmt.Println("Хотя бы одно число чётное")
} else {
fmt.Println("Оба числа нечётные")
}
Также допускается создание вложенных условий. Но стоит помнить, что высокий уровень вложенности усложняет программу и затрудняет ее понимание.
Например, с помощью вложенных условий можно реализовать простейшую систему аутентификации пользователя:
var login, pass string
fmt.Scan(&login)
if login == "Gosha" {
fmt.Println("Логин верный. Введите пароль.")
fmt.Scan(&pass)
if pass == "ilovego" {
fmt.Println("Пароль верный! Успешный вход!")
} else {
fmt.Println("Пароль неверный! Попробуйте еще раз.")
}
} else {
fmt.Println("Неверный логин")
}
В этом примере мы запрашиваем ввод логина и далее сравниваем введенное значение со строкой "Gosha"
. В случае соответствия логина выводим сообщение и ожидаем ввод пароля, иначе выводим "Неверный логин"
. После проверки пароля на равенство строке "ilovego"
печатаем сообщение либо об успешном входе, либо о неверном пароле.
Обработка ошибок
В Go оператор if
часто используется для обработки ошибок. Чтобы продемонстрировать это поведение, воспользуемся функцией strconv.Atoi
, которая возвращает два значения – результат перевода строки в число и ошибку.
num, err := strconv.Atoi(“a”)
if err != nil {
return err // один из вариантов обработки ошибки
}
Для проверки наличия ошибки используется оператор if
, который сравнивает значение переменной err
с нулевым (nil
). Если err != nil
, то это говорит о возникновении ошибки, которую нужно обработать в блоке оператора if
.
Switch-case
Оператор switch-case
выполняет те же задачи, что и if-else
, но позволяет записывать условные выражения в более коротком виде. Он состоит из четырех ключевых слов: switch
– для создания оператора, case
– для проверки условий (аналог if
), default
– для задания значения по умолчанию в случае ложности предыдущих условий (аналог else
), fallthrough
– для принудительного выполнения следующего блока case.
Синтаксис switch-case
выглядит следующим образом:
switch переменная {
case значение1:
// блок кода, если переменная имеет значение1
case значение2:
// блок кода, если переменная имеет значение2
...
default:
// блок кода, если переменная не равна ни одному из проверенных значений
}
Пример применения switch-case
с указанием переменной для проверки:
num := 0
switch num {
case 0:
fmt.Println("Число равно нулю")
default:
fmt.Println("Число не равно нулю")
}
Все операторы case выполняются последовательно. Но что если необходимо обязательно учесть определенное условие? В таком случае используется ключевое слово fallthrough
. Оно ставится в конце блока case и указывает на то, что следующий оператор case будет выполнен независимо от истинности своего условия.
Пример switch-case
без переменной для проверки и с ключевым словом fallthrough
:
num := 3
switch {
case num < 5:
fmt.Println("Число меньше пяти")
fallthrough // если число меньше 5, то оно точно меньше 10, поэтому здесь можно указать fallthrough
case num < 10:
fmt.Println("Число меньше десяти")
fallthrough
case num < 100:
fmt.Println("Число меньше ста")
default:
fmt.Println("Число больше пяти")
}
В результате будет выведено:
Число меньше пяти
Число меньше десяти
Число меньше ста
Цикл for

Циклы наряду с условными конструкциями являются составными частями большинства программ и позволяют выполнять повторяющиеся операции заданное количество раз. Это бывает полезно при работе со структурами данных, например для перебора значений массива или ключей мапы.
Основным элементом цикла является итератор – переменная, которая по заданному правилу изменяет свое значение.
В отличие от других языков, Go для создания цикла имеет только один оператор for
. В общем случае его синтаксис выглядит так:
for инициализация итератора; условие для итератора; шаг итератора {
// код в цикле
}
В качестве примера посчитаем квадраты чисел от 1 до 9 с использованием цикла:
for i := 1; i < 10; i++ {
fmt.Println(i * i)
}
Для достижения необходимого результата бывает полезно изменять параметры цикла. К примеру, отрицательный шаг позволит перебрать значения в обратном порядке, но в таком случае начальное значение должно быть больше конечного:
// вывод квадратов чисел от 10 до 1
for i := 10; i >= 0; i-- {
fmt.Println(i * i)
}
Шаг необязательно должен изменяться на единицу. Вместо этого можно использовать любое значение:
// вывод чётных чисел от 30 до 1
for i := 30; i > 0; i -= 2 {
fmt.Println(i)
}
Как было упомянуто ранее, циклы часто используются для итерации по массивам. Это реализуется с использованием ключевого слова range, которое задает диапазон перебираемых значений:
array := []int{10, 20, 30, 40, 50}
for index, val := range array {
fmt.Printf("Элемент с индексом %d: %d\\n", index, val) // форматированный вывод
}
Обратите внимание, что первой переменной в таком цикле является индекс, а второй – значение.
При объявлении цикла for
необязательно указывать все параметры. Такая особенность была введена неслучайно, ведь это делает возможным реализацию второго типа циклов – while
(цикл «пока»). Он выполняется до тех пор, пока заданное условие истинно.
Давайте перепишем предыдущий код для подсчета квадратов чисел от 1 до 9 в стиле цикла while
:
i := 1
for i < 10 {
fmt.Println(i * i)
i++
}
При работе с таким циклом стоит следить за выполнением условия выхода и правильно изменять итератор, иначе есть риск получить бесконечный цикл, способный привести к зависанию или полному краху программы:
// ошибка: бесконечный цикл
for {
fmt.Println("brrr")
}
Помимо проверки условия есть другой подход для выхода из цикла – оператор break
. Он принудительно завершает выполнение цикла. Приведенный ниже код печатает квадраты чисел от 1 до 4 включительно, при этом break
обеспечивает досрочный выход из for при достижении итератором числа 5:
for i := 1; i < 10; i++ {
if i == 5 {
break
}
fmt.Println(i * i)
}
Несмотря на кажущуюся непригодность, бесконечные циклы бывают полезны для обеспечения бесперебойной работы программы. Чтобы это проиллюстрировать, напишем код для непрерывного считывания данных с консоли до ввода определённого символа:
for {
var input string
fmt.Scan(&input) // ввод данных
if input == "q" { // условие выхода из цикла
break
}
}
Но если есть оператор, останавливающий цикл, то должен быть и продолжающий! И он есть – это оператор continue
, досрочно переходящий к следующей итерации. Он бывает полезен для пропуска определенных значений. Например, код ниже выведет на экран квадраты всех чисел от 1 до 20, не делящихся на 3 или 7:
for i := 1; i <= 20; i++ {
if i%3 == 0 || i%7 == 0 {
continue // досрочный переход к следующей итерации цикла
}
fmt.Println(i * i)
}
Вложенные циклы
Циклы, как и условные конструкции, могут быть вложенными. Это полезный инструмент для генерации комбинаторных объектов и обработки многомерных структур данных, таких как n-мерные массивы, деревья и графы.
Простым примером вложенного цикла является программа для вывода на экран таблицы умножения чисел от 1 до 10:
for i := 1; i <= 10; i++ {
for j := 1; j <= 10; j++ {
fmt.Print(i*j, "\\t")
}
fmt.Println()
}
При работе с вложенными конструкциями всегда стоит помнить об их отрицательном влиянии на производительность и читаемость кода.
Чтобы проиллюстрировать влияние вложенных циклов на производительность, обратимся к примеру с тройным уровнем вложенности, где каждый цикл выполняется n раз, и посчитаем количество итераций:
n, iterations := 100, 0
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
for k := 0; k < n; k++ {
iterations++
}
}
}
fmt.Println("Количество итераций: ", iterations)
В примере выше количество операций будет 1003, а в общем случае говорят, что такая программа имеет сложность «порядка n в кубе» или O(n3). На маленьких значениях разница во времени выполнения может быть несущественна, но уже для n = 1000000
программа будет исполнятся критически долго, что недопустимо в условиях реальной разработки. Поэтому стоит избегать необдуманного использования вложенных циклов и оптимизировать их с помощью эффективных алгоритмов.
Для знакомства с понятием сложности алгоритмов рекомендуется прочитать статью Асимптотическая сложность алгоритмов: что за зверь?
Задачи
Пришло время применить изученную теорию на практике. Предлагаем решить несколько несложных задач на условные конструкции и циклы.
Задача 1: Гоша учится считать

Первокласснику Гоше на уроке математики дали задание: посчитать сумму всех чисел от 20 до 500, которые делятся на 6, и при этом не делятся на 8. Помогите Гоше решить эту задачу.
Решение:
var sum int
for i := 20; i <= 500; i++ {
if i % 6 == 0 && i % 8 != 0 {
sum += i
}
}
fmt.Println(sum)
Задача 2: Гоша изобретает калькулятор

Гоша хочет облегчить себе жизнь и создать собственный калькулятор на языке Go, чтобы быстро производить базовые операции над числами.
Входные данные: в первой строке вводятся два целых числа, а на следующей – один из четырех возможных символов, обозначающих математическую операцию: +
, -
, *
, /
Выходные данные: результат применения операции к числам, в случае неверного ввода возвращается сообщение: ««Ошибка, введите два целых числа и одну из четырех допустимых операций: +, -, *, /»
Решение: используем условную конструкцию switch-case
var a, b int
fmt.Scan(&a, &b)
var operation string
fmt.Scan(&operation)
switch operation {
case "+":
fmt.Println(a + b)
case "-":
fmt.Println(a - b)
case "*":
fmt.Println(a * b)
case "/":
fmt.Println(a / b)
default:
fmt.Println("Ошибка, введите два целых числа и одну из четырех допустимых операций: +, -, *, /")
}
Задача 3: Гоша учится играть в шахматы

Гоша решил сыграть партию в шахматы со своим одноклассником, но забыл, как ходит ладья. Помогите Гоше определить, может ли ладья за один ход попасть с первой клетки на вторую.
Входные данные: четыре числа от 1 до 8 – номер столбца и номер строки сначала для первой клетки, а потом для второй.
Выходные данные: "YES", если из первой клетки можно за один ход попасть во вторую, иначе "NO".
Решение: для проверки условия достаточно сравнить пары координат клеток по вертикали и горизонтали. Если хотя бы одна пара совпадает, то ладья сможет попасть из одной клетки в другую, иначе – нет.
var x1, y1, x2, y2 int
fmt.Scan(&x1, &y1, &x2, &y2)
if x1 == x2 || y1 == y2 {
fmt.Println("YES")
} else {
fmt.Println("NO")
}
Задача 4: Гоша считает делители

Гоше стало интересно, как можно найти сумму делителей заданного числа n. Давайте поможем ему решить эту задачу, написав алгоритм на Go.
Входные данные: целое число n.
Выходные данные: сумма делителей числа n.
Решение: достаточно в цикле проверить деление n на все числа от 1 до n / 2, не забыв учесть само число n.
var n, sum int
fmt.Scan(&n)
for i := 1; i < n/2+1; i++ {
if n%i == 0 {
sum += i
}
}
sum += n // учитываем само число
fmt.Println(sum)
Задача 5: Гоша решает задачу со звездочкой

Гоше поручили написать код для детской игры FizzBuzz, правила которой следующие: игроки называют числа подряд, если число делится на 3, его заменяют на «Fizz», если делится на 5, то заменяют на «Buzz», а если делится на 3 и на 5 одновременно, то заменяют на «FizzBuzz». Ваша задача вывести все числа от 1 до 200 по правилам этой игры.
Решение: сразу оговоримся, что у этой задачи есть несколько решений. Самое очевидное заключается в рассмотрении всех возможных случаев, для каждого из которых выводится соответствующая строка. В представленном здесь решении используется похожий подход: каждую итерацию цикла создается строка, к которой приписывается определенное значение, зависящее от делимости итератора на 3 и 5. Если он не делится ни на одно из этих чисел, то к строке припишется строковое значение итератора.
for i := 1; i <= 200; i++ {
var s string // заново инициализируем пустую строку
if i%3 == 0 {
s += "Fizz"
}
if i%5 == 0 {
s += "Buzz"
}
if s == "" { // если текущее число не делится на 3 и на 5
s += fmt.Sprint(i) // записываем в строку текущее число
}
fmt.Println(s) // выводим строку
}
Подведём итоги
В этой части самоучителя по Go мы познакомились с фундаментальными понятиями в программировании – условными конструкциями и циклами. Их понимание позволит в дальнейшем создавать гибкие и функциональные программы.
В следующем уроке рассмотрим функции, аргументы, рекурсию и defer,
а в конце по традиции закрепим теорию интересными задачами.
Содержание самоучителя
- Особенности и сфера применения Go, установка, настройка
- Ресурсы для изучения Go с нуля
- Организация кода. Пакеты, импорты, модули. Ввод-вывод текста.
- Переменные. Типы данных и их преобразования. Основные операторы
- Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
- Функции и аргументы. Области видимости. Рекурсия. Defer
- Массивы и слайсы. Append и сopy. Пакет slices
- Строки, руны, байты. Пакет strings. Хеш-таблица (map)
- Структуры и методы. Интерфейсы. Указатели. Основы ООП
- Наследование, абстракция, полиморфизм, инкапсуляция
- Обработка ошибок. Паника. Восстановление. Логирование
- Обобщенное программирование. Дженерики
- Работа с датой и временем. Пакет time
- Интерфейсы ввода-вывода. Буферизация. Работа с файлами. Пакеты io, bufio, os
- Конкурентность. Горутины. Каналы
- Тестирование кода и его виды. Table-driven подход. Параллельные тесты
- Основы сетевого программирования. Стек TCP/IP. Сокеты. Пакет net
- Протокол HTTP. Создание HTTP-сервера и клиента. Пакет net/http
Комментарии