JavaRush /Курсы /Go SELF /Единственный цикл Go — for

Единственный цикл Go — for

Go SELF
4 уровень , 1 лекция
Открыта

1. Зачем нужны циклы и почему в Go он один

Если вы хоть раз пытались «просто повторить действие 10 раз» вручную, вы знаете, как это обычно заканчивается: десять одинаковых строк, и на одиннадцатой вы ошиблись в имени переменной. Циклы — это способ сказать компьютеру: «сделай вот этот блок кода несколько раз, и давай договоримся, когда остановиться».

Цикл в реальной жизни: Пока Барни в баре — Мо выбрасывает его.

С точки зрения здравого смысла, цикл — это управляющая конструкция, которая повторяет тело (блок { ... }) пока выполняется некое правило. В Go это особенно приятно: цикл всего одинfor. Да-да, никакого while как отдельного слова. Go как бы говорит: «Зачем вам два слова, если можно одно?».

for в Go: одна конструкция на разные задачи

Важно не пугаться фразы «единственный цикл». Это не означает, что Go «бедный» — это означает, что for умеет несколько форм записи. Но сегодня мы берём самую классическую и самую полезную для новичка: цикл со счётчиком.

В реальной жизни это выглядит как «сделай 5 раз», «пройди от 1 до N», «выведи числа от 0 до 9», «прочитай N значений». Почти все такие задачи удобно выражаются классическим for, где есть начало, условие и шаг.

2. Классический for: init; cond; post

Общая форма цикла

Эта форма — как классический бутерброд: три слоя с понятными ролями. Выглядит она так:

for init; cond; post {
    // тело цикла
}

В Go такую форму вы будете видеть постоянно — даже в примерах из официальных материалов, например в статье про defer, где внутри цикла откладывают печать значений. Там как раз используется привычный счётчик i := 0; i < 4; i++, то есть ровно та конструкция, которую мы сейчас разбираем.

Теперь разложим по деталям (без нервов, всё логично).

init: подготовка перед стартом

В секции init обычно создают и инициализируют счётчик. Причём это делается один раз — перед первой проверкой условия.

Чаще всего там пишут i := 0 или i := 1. Почему иногда ноль, а иногда один — это вопрос удобства и того, как вы мысленно считаете. Компьютеру всё равно, человеку — нет. Если вы нумеруете «попытки 1..N», обычно стартуют с 1. Если вы работаете с индексами (что мы позже увидим в массивах/слайсах), часто стартуют с 0.

Мини-пример: объявили счётчик и подготовились печатать.

package main

import "fmt"

func main() {
	for i := 0; i < 3; i++ {
		fmt.Println("Итерация", i) // Итерация 0 / 1 / 2
	}
}

Обратите внимание на деталь: i объявлен через := прямо внутри for. Это нормально и очень типично: счётчик живёт внутри цикла и почти никогда не нужен снаружи.

cond: условие остановки

cond (condition) — это выражение типа bool, которое проверяется перед каждой итерацией. Если условие истинно — выполняется тело цикла. Если ложно — цикл завершается, и программа продолжает выполнение после цикла.

Важно почувствовать простую мысль: условие — это «охранник на входе». Он не пускает вас на очередную итерацию, если правило уже не выполняется.

Пример на «выведи числа 1..5» (включительно):

package main

import "fmt"

func main() {
	for i := 1; i <= 5; i++ {
		fmt.Println(i) // 1 2 3 4 5
	}
}

Здесь условие i <= 5 означает: «пока i не больше 5 — работаем».

Тело цикла: что именно повторяем

Тело цикла — это обычный блок кода в фигурных скобках. Вы уже видели такие блоки в if/else и switch. В цикле блок выполняется много раз — и это единственное отличие.

Хорошая привычка: внутри тела цикла держать код максимально простым. Если внутри цикла слишком много всего, становится сложно понять, сколько раз и что именно происходит. Пока мы новички — мы пишем маленькими понятными кусочками, а «большие простыни» оставим людям, которые любят страдать.

Простейший пример тела: вывести что-то несколько раз.

package main

import "fmt"

func main() {
	for i := 0; i < 2; i++ {
		fmt.Println("Go — это нормально") // Go — это нормально (2 раза)
	}
}

post: шаг между итерациями

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

Если вы забыли менять счётчик (или меняете не туда) — цикл может стать бесконечным. Но паниковать рано: это типичная ошибка новичка, и мы её научимся ловить глазами.

Мини-пример: шаг на 2.

package main

import "fmt"

func main() {
	for i := 0; i < 6; i = i + 2 {
		fmt.Println(i) // 0 2 4
	}
}

Здесь post — это i = i + 2. Он выполняется после каждого fmt.Println.

i++ и i--: короткая запись «плюс один» и «минус один»

В Go для счётчиков есть два очень узнаваемых символа из мира «классических циклов»: i++ и i--. Это короткая запись для «увеличить на 1» и «уменьшить на 1».

Важный момент для чтения кода: когда вы видите i++, почти всегда это означает «обычный счётчик, обычная итерация».

Пример с уменьшением:

package main

import "fmt"

func main() {
	for i := 3; i > 0; i-- {
		fmt.Println(i) // 3 2 1
	}
}

Здесь условие i > 0 и шаг i-- согласованы: мы идём вниз к нулю, и цикл гарантированно остановится.

Порядок выполнения: initcondbodypost

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

Сначала выполняется init (ровно один раз). Потом проверяется cond. Если cond == true, выполняется тело body. Затем выполняется post. После этого цикл возвращается к проверке cond. И так по кругу, пока условие не станет ложным.

Вот схема, которую полезно помнить (как «карту метро»):

flowchart TD
    A[init: один раз] --> B{cond?}
    B -- true --> C[body: тело]
    C --> D[post: шаг]
    D --> B
    B -- false --> E[после цикла]

Если вы не любите схемы, можно держать это как таблицу:

Элемент Когда выполняется Типичное содержимое
init
один раз, в начале
i := 0
cond
перед каждой итерацией
i < n
body
если
cond == true
полезная работа
post
после тела
i++

3. Полезные привычки и важные нюансы

Привычка: понимать, какие значения примет i

Это супер-навык для новичка: уметь ответить на вопрос «какие значения примет счётчик». Не “примерно”, а прям конкретно. Иначе вы будете постоянно попадать в ситуацию «я ожидал 5 повторов, а получил 6».

Разберём на примере:

for i := 0; i < 5; i++ { ... }

Счётчик стартует с 0. Условие: i < 5. Значит i будет: 0, 1, 2, 3, 4. Пятёрка уже не войдёт, потому что на i == 5 условие ложно.

А вот тут:

for i := 1; i <= 5; i++ { ... }

Счётчик: 1, 2, 3, 4, 5. Здесь 5 входит, потому что условие «меньше или равно».

Это кажется мелочью, но на практике это половина успеха в циклах.

Почему i обычно объявляют прямо в for

Когда вы пишете for i := 0; ..., вы как бы говорите: «счётчик — служебная штука, пусть живёт только тут». Это помогает не засорять main лишними переменными, которые потом случайно используют не по назначению.

Ещё это делает код более “самодокументируемым”: вы открываете файл, видите цикл и сразу понимаете, где появился i и как он меняется. Никаких «а кто такой i и почему он вдруг равен 17» (хотя, конечно, иногда жизнь всё равно подкидывает сюрпризы).

init, cond, post — части одного механизма

Очень частая ошибка начинающего — воспринимать init, cond, post как три формальности: мол, «все так пишут». На самом деле это три детали одного механизма управления.

init отвечает за стартовое состояние. cond отвечает за правило «когда продолжаем». post отвечает за движение состояния к завершению. Если хоть одна часть выбрана неудачно, цикл либо сделает не то количество итераций, либо вообще «залипнет».

Супер-простая проверка здравого смысла: попробуйте ответить на два вопроса. Какая переменная меняется на каждой итерации? И как она приближает нас к моменту, когда cond станет ложным? Если на эти вопросы нет ответа — цикл подозрительный.

4. Пример: TaskBox

Чтобы не учить for просто так, давайте начнём собирать маленькое приложение, которое будет расти вместе с курсом. Назовём его TaskBox: пока это совсем простой CLI, который помогает пользователю вводить задачи и видеть их с нумерацией.

Сегодня у нас ещё нет массивов и слайсов, поэтому «хранить список задач» мы пока не умеем. Но уже можем сделать полезную вещь: повторить ввод N раз и красиво пронумеровать то, что ввели. Это уже похоже на настоящее приложение, а не на «выведи hello 10 раз».

Читаем N и печатаем номера задач

Этот подшаг помогает почувствовать «сколько раз повторится цикл» и как выглядит нумерация.

package main

import "fmt"

func main() {
	var n int
	fmt.Scan(&n)

	for i := 1; i <= n; i++ {
		fmt.Println("Задача №", i) // при n=3: Задача № 1 / 2 / 3
	}
}

Здесь i — это человеческая нумерация. Мы специально выбрали старт с 1, чтобы вывод выглядел дружелюбно.

Повторяем ввод N раз и печатаем то, что ввели

Теперь добавим «имя задачи». Напомню: fmt.Scan читает токены, то есть для простоты пусть задача будет одним словом (например, buy_milk). Это нормально для учебного шага.

package main

import "fmt"

func main() {
	var n int
	fmt.Scan(&n)

	for i := 1; i <= n; i++ {
		var title string
		fmt.Scan(&title)
		fmt.Println(i, "-", title) // пример: 1 - buy_milk
	}
}

Это уже практический паттерн: цикл управляет количеством повторов, а внутри мы делаем одинаковую операцию (читаем и печатаем).

5. Ещё один пример: таблица квадратов

Иногда полезно увидеть, что цикл — это не только «ввод/вывод», но и вычисления. Здесь мы печатаем таблицу квадратов от 1 до N. Никаких новых конструкций — только for и умножение.

package main

import "fmt"

func main() {
	var n int
	fmt.Scan(&n)

	for i := 1; i <= n; i++ {
		fmt.Println(i, "->", i*i) // при n=3: 1->1, 2->4, 3->9
	}
}

Это упражнение (в смысле “пример”) особенно хорошо тем, что вы можете быстро глазами проверить корректность результата. Если на месте 3 вдруг появилось 16 — значит, вы где-то «съехали» со счётчиком.

6. Типичные ошибки при написании классического for

Ошибка №1: перепутать < и <= и получить лишнюю или недостающую итерацию.
Это не «невнимательность», это реально самая частая история. Мозг читает i < 5 и думает «ну, до пяти», а компьютер читает буквально: «строго меньше пяти». Лечится привычкой проговаривать: «последнее значение i будет …».

Ошибка №2: неправильный старт счётчика (например, начали с 0, а выводите “попытка №0”).
Технически код верный, но пользователь будет смотреть на “попытка №0” с подозрением, как на чек из магазина с “-1 товар”. Для человеческой нумерации чаще выбирают старт с 1. Для индексирования (когда оно появится позже) часто выбирают старт с 0 — но это именно “позже”, а не “везде всегда”.

Ошибка №3: шаг не согласован с условием.
Если условие i < n, а шаг i--, то вы никогда не приблизитесь к i >= n. Это как ехать на машине к финишу, но каждый раз после поворота уезжать в противоположную сторону. Компилятор не обязан спасать вас от логики — он только следит за синтаксисом.

Ошибка №4: попытка «сложить всё сразу» в одну строку цикла.
Иногда новички пытаются сделать “круто”: инициализация сложная, условие хитрое, шаг с несколькими переменными — и всё это сразу. В итоге цикл становится загадкой даже для автора через пять минут. На старте лучше держать init простым (i := 0), cond понятным (i < n) и post очевидным (i++). Красота придёт, когда мозг перестанет тратить силы на базовую механику.

1
Задача
Go SELF, 4 уровень, 1 лекция
Недоступна
Лифт по этажам
Лифт по этажам
1
Задача
Go SELF, 4 уровень, 1 лекция
Недоступна
Чат с подписью
Чат с подписью
1
Задача
Go SELF, 4 уровень, 1 лекция
Недоступна
Касса и сдача
Касса и сдача
1
Задача
Go SELF, 4 уровень, 1 лекция
Недоступна
Рамка интерфейса
Рамка интерфейса
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Aleksandr Bobrus Уровень 4
22 мая 2026
В задачах отсутствуют проверки на ошибки, получается примитивно.
Georgy Уровень 1
14 мая 2026
а что ошибки обработать потенциальные не?) уже не надо пишите ?)))
Мишаня Уровень 15
11 мая 2026
В задаче "Касса и сдача" неправильно указано условие для цикла, поправьте. А то получается что ты вводишь 3 значения, а читаются только 2)