Shanraq.org Shanraq.org
Ветвление и цикл в Go: if, else и единственный for
IT

Go: с нуля до своего блога Урок 6 из 50

Ветвление и цикл в Go: if, else и единственный for

Шестой урок курса по Go. Как программа выбирает между двумя путями: if, else if и else, сравнения и логические И, ИЛИ, НЕ. И цикл — в Go он ровно один, слово for, зато в трёх формах: со счётчиком, по условию и без условия с break. Ни while, ни do-while, ни foreach в языке нет.

Зачем это нужно

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

А чтобы повторить действие, не переписывая его десять раз, нужен цикл. И вот здесь Go устроен непривычно: цикл в нём один. Слова while, do-while и foreach в языке отсутствуют — всё это делает for, просто в разных формах.

Сразу целиком

Новая папка, go mod init sabaq05, main.go:

package main

import "fmt"

func label(views int) string {
	if views >= 1000 {
		return "популярное"
	} else if views >= 100 {
		return "читают"
	}
	return "новое"
}

func main() {
	fmt.Println(12, "—", label(12))
	fmt.Println(340, "—", label(340))
	fmt.Println(1500, "—", label(1500))

	for i := 1; i <= 3; i++ {
		fmt.Println("урок", i)
	}

	words := 0
	for words < 600 {
		words += 200
	}
	fmt.Println("накопилось слов:", words)
}

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

go run . печатает:

12 — новое
340 — читают
1500 — популярное
урок 1
урок 2
урок 3
накопилось слов: 600

Разбор

if: развилка

if views >= 1000 {

Условие пишется без круглых скобок — в Go они не нужны и их не ставят. Тело всегда в фигурных, даже если внутри одна строка: сокращённой записи без скобок в языке нет, и это избавляет от целого класса ошибок, на которых спотыкаются в других языках.

Образ. Развилка на дороге со знаком. Знак ничего не делает — он только решает, по какой стороне вы поедете дальше. Обе дороги существуют, поехать можно лишь по одной.

else if и else

Проверки идут сверху вниз, и выполняется первая подошедшая. Порядок поэтому важен: если поставить >= 100 выше >= 1000, то тысяча попадёт в «читают» и до второй проверки дело не дойдёт.

else в конце не обязателен. В примере его нет: после всех проверок стоит просто return "новое" — тот же смысл, на строку короче.

Чем сравнивают

== равно, != не равно, < > <= >=. Двойное равенство в сравнении и одинарное в присваивании — разные вещи, и Go не даст их перепутать: if views = 1000 не соберётся.

Условия соединяют: && — и то, и другое; || — хотя бы одно; ! — наоборот.

if views >= 100 && views < 1000 {

for со счётчиком

for i := 1; i <= 3; i++ {

Три части через точку с запятой: завести счётчик, пока проверять, что делать после каждого круга. i++ — увеличить на единицу.

Образ. Три вещи, которые говорят кладовщику: с какой полки начать, до какой считать и через сколько шагать. Скажете все три — он обойдёт склад сам.

for по условию

for words < 600 {

Одна часть вместо трёх. В других языках это называется while; в Go это тот же for, просто без счётчика и без шага.

for без условия

for {
	// пока не скажут выйти
}

Крутится вечно, пока внутри не встретится break. Так пишут серверы и ожидание событий.

break выходит из цикла совсем. continue бросает текущий круг и начинает следующий.

Образ. Очередь. break — уйти из очереди насовсем. continue — пропустить свой шаг и сразу перейти к следующему, не доделав то, что собирались.

if с коротким объявлением

Здесь возвращается прошлый урок. Функция отдаёт два значения, и оба нужны только внутри проверки:

if minutes, ok := readingTime(700); ok {
	fmt.Println(minutes)
}

Точка с запятой отделяет объявление от условия. minutes и ok живут только внутри этого if и снаружи не существуют — а значит, не мешают и не путаются с другими именами. В Go так пишут почти всегда, когда результат нужен ровно на одну проверку.

Четвёртая форма — потом

У for есть ещё одна запись, range: она обходит то, что состоит из частей — строку по буквам, список по элементам. Со строкой встретимся уже в следующем уроке, со списком — в уроке про срезы.

Функция может позвать сама себя

Цикл — не единственный способ повторить. Функция может вызвать саму себя, и называется это рекурсией:

func countdown(n int) {
	if n == 0 {
		fmt.Println("поехали")
		return
	}
	fmt.Println(n)
	countdown(n - 1)
}

func main() {
	countdown(3)
}
3
2
1
поехали

Здесь две части, и обе обязательны. Первая — выход: if n == 0 и return, случай, в котором функция больше себя не зовёт. Вторая — шаг, который к этому выходу приближает: countdown(n - 1), каждый раз с числом поменьше.

Уберите выход — и программа не остановится. Go не даст ей висеть вечно, но и не поблагодарит:

runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

Каждый вызов занимает место в памяти, и когда место кончается, программа падает. Цикл for так упасть не может: он не копит вызовы.

Образ. Матрёшка. Вы открываете её, внутри такая же поменьше, и так до самой маленькой — цельной. Если бы маленькой не было, вы бы открывали до бесконечности.

Когда её выбирают вместо цикла: когда данные вложены сами в себя. Комментарий к комментарию, папка в папке, ответ на ответ — обойти такое циклом трудно, а рекурсией естественно. Для плоского списка цикл проще и быстрее, и в этом курсе почти везде будет он.

Карта урока

Карта урока: одно слово for, три формы

Скажите своими словами

Не подглядывая, ответьте вслух или на бумаге. Ответы — в конце урока.

  1. Почему в label порядок проверок нельзя поменять местами?
  2. Чем break отличается от continue?
  3. Зачем писать if n, ok := f(); ok, если можно объявить n и ok строкой выше?

Задание

Обязательное. Напишите функцию label(views int) string: при 1000 и больше возвращает "популярное", при 100 и больше — "читают", иначе "новое". Вызовите её из main для 12, 340 и 1500 и напечатайте результат.

По желанию.

  • Цикл со счётчиком от 1 до 10, который печатает только чётные числа. Подсказка: остаток от деления — %, чётное значит i%2 == 0.
  • Вернитесь к заданию прошлого урока, которое ждало if: сделайте, чтобы readingTime возвращала два значения — минуты и bool, разумное ли число слов.
  • Цикл for без условия, который выходит по break на пятом круге.

Куда это встанет в блоге

Ветвление в блоге повсюду: показать статью или ответить 404, пустить редактировать или отказать, вывести «нет комментариев» или список. А цикл понадобится, как только статей станет больше одной, — и в уроке про срезы вы обойдёте их все одной строкой.

Ответы

Показать ответы
  1. Потому что выполняется первая подошедшая проверка, а не самая точная. Тысяча просмотров удовлетворяет и >= 100, и >= 1000; если сначала стоит сотня, до тысячи дело не дойдёт и «популярное» не выпадет никогда. Условия ставят от узкого к широкому.
  2. break заканчивает цикл целиком, и выполнение идёт дальше за ним. continue заканчивает только текущий круг: остаток тела пропускается, но цикл продолжается со следующего значения.
  3. Чтобы имена не жили дольше, чем нужны. Объявленные строкой выше, n и ok останутся до конца функции, будут мешать назвать что-то так же и создадут иллюзию, что ими можно пользоваться дальше. В if они исчезают на закрывающей скобке.

Источники

Если вы нашли ошибку или опечатку в тексте статьи, то сообщите нам об этом

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

Сначала решите и запустите в VS Code — редактор покажет ошибку на месте. Готовое решение вставьте сюда. Проверяет модель: она укажет на ошибку, но не даст готовый ответ.

Чтобы проверить, нужно войти. Войти

Комментарии (0)

Пока нет комментариев. Будьте первым.