Цикл for как while и бесконечный цикл
Типы циклов в программировании
В большинстве языков программирования существует три типа циклов:
- Цикл со счётчиком —
for(классический) - Цикл с предусловием —
while - Цикл с постусловием —
do-while
Особенность Go: нет отдельных операторов while и do-while. Вместо этого один оператор for может работать во всех трёх режимах.
For как while: цикл с предусловием
Синтаксис
// Классический for
for init; condition; post {
// тело
}
// For как while (опускаем init и post)
for condition {
// тело
}
Трансформация из классического for в while
Шаг 1: Классический for
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Шаг 2: Выносим инициализацию наружу
i := 0
for ; i < 5; i++ {
fmt.Println(i)
}
Шаг 3: Выносим приращение в тело
i := 0
for ; i < 5; {
fmt.Println(i)
i++
}
Шаг 4: Убираем точки с запятой → получаем while
i := 0
for i < 5 {
fmt.Println(i)
i++
}
Пример: обратный отсчёт
countdown := 5
for countdown >= 0 {
fmt.Printf("%d ", countdown)
countdown--
}
// Вывод: 5 4 3 2 1 0
Порядок выполнения:
- Инициализация
countdown = 5(вне цикла) - Проверка условия:
5 >= 0→ true - Печать
5 - Приращение:
countdown--→countdown = 4 - Проверка условия:
4 >= 0→ true - Повторение до
countdown = -1→ выход
Когда использовать for как while
Ситуация 1: Неизвестное количество итераций
sum := 0
iterations := 0
for sum < 50 {
value := rand.Intn(10) // Случайное 0-9
sum += value
iterations++
fmt.Printf("Итерация %d: +%d → сумма = %d\n", iterations, value, sum)
}
Пример вывода:
Итерация 1: +3 → сумма = 3
Итерация 2: +7 → сумма = 10
Итерация 3: +8 → сумма = 18
Итерация 4: +9 → сумма = 27
Итерация 5: +6 → сумма = 33
Итерация 6: +2 → сумма = 35
Итерация 7: +8 → сумма = 43
Итерация 8: +9 → сумма = 52
Цикл завершится, когда sum >= 50, но заранее неизвестно, сколько потребуется итераций.
Ситуация 2: Обработка пользовательского ввода
attempts := 0
maxAttempts := 3
for attempts < maxAttempts {
randomNum := rand.Intn(100)
attempts++
fmt.Printf("Попытка %d: число %d\n", attempts, randomNum)
if randomNum > 80 {
fmt.Println("✓ Успех!")
break // Досрочный выход
}
}
Пример вывода:
Попытка 1: число 34
Попытка 2: число 67
Попытка 3: число 89
✓ Успех!
Сравнение: Go vs другие языки
| Язык | Синтаксис while | Синтаксис do-while |
|---|---|---|
| Java/C#/C++ | while (x > 0) { } |
do { } while (x > 0); |
| Python | while x > 0: |
❌ Нет |
| JavaScript | while (x > 0) { } |
do { } while (x > 0); |
| Go | for x > 0 { } |
❌ Нет (эмулируется) |
Философия Go: минимализм — один оператор for для всех случаев.
Бесконечный цикл: for { }
Синтаксис
for {
// тело цикла
// выход только через break
}
Пример: печать символов
for {
fmt.Print("+")
}
// Вывод: ++++++++++++++++... (бесконечно)
// Остановка: Ctrl+C
Выход через break
count := 0
for {
count++
fmt.Printf("Итерация %d\n", count)
if count >= 3 {
fmt.Println("Выходим через break")
break // Выход из цикла
}
}
Вывод:
Итерация 1
Итерация 2
Итерация 3
Выходим через break
Эмуляция do-while (цикл с постусловием)
Что такое do-while
Do-while — цикл, который сначала выполняет тело, затем проверяет условие.
Классический do-while (Java/C#):
do {
// тело
} while (condition);
Эмуляция в Go
i := 1
for {
fmt.Print("+") // Тело выполняется СНАЧАЛА
i++
if i >= 5 { // Проверка ПОСЛЕ тела
break
}
}
// Вывод: ++++ (4 плюса)
Порядок выполнения:
- Печать
+(i=1) - Приращение
i++→ i=2 - Проверка
i >= 5→ false → продолжение - Печать
+(i=2) - Приращение
i++→ i=3 - Проверка
i >= 5→ false → продолжение - Печать
+(i=3) - Приращение
i++→ i=4 - Проверка
i >= 5→ false → продолжение - Печать
+(i=4) - Приращение
i++→ i=5 - Проверка
i >= 5→ true → break → выход
Нужно ли это использовать?
Ответ: скорее всего, нет.
Причины:
- Код сложно читать
- 95-99% задач решаются классическим
for - Эмуляция do-while не идиоматична для Go
Оператор break: ручник цикла
Назначение
break немедленно прерывает выполнение цикла.
Пример: остановка при числе > 7
j := 0
for j < 10 {
randomVal := rand.Intn(10)
fmt.Printf("%d ", randomVal)
if randomVal > 7 {
fmt.Println("\nЧисло > 7, выходим!")
break // Выход из цикла
}
j++
}
Пример вывода:
3 5 2 8
Число > 7, выходим!
Работа с дебаггером
Трассировка:
| Итерация | j | randomVal | Условие > 7 |
Действие |
|---|---|---|---|---|
| 1 | 0 | 3 | false | Продолжение |
| 2 | 1 | 5 | false | Продолжение |
| 3 | 2 | 2 | false | Продолжение |
| 4 | 3 | 8 | true | break → выход |
После break всё, что следует в теле цикла, игнорируется.
Оператор continue: пропуск итерации
Назначение
continue пропускает остаток текущей итерации и переходит к следующей.
Пример: печать только нечётных чисел
i := 0
for i < 10 {
i++
if i%2 == 0 {
continue // Пропускаем чётные
}
fmt.Printf("%d ", i)
}
// Вывод: 1 3 5 7 9
Трассировка
| i | i%2 == 0 |
Действие | Вывод |
|---|---|---|---|
| 1 | false | Печать | 1 |
| 2 | true | continue → пропуск | — |
| 3 | false | Печать | 3 |
| 4 | true | continue → пропуск | — |
| 5 | false | Печать | 5 |
| 6 | true | continue → пропуск | — |
| 7 | false | Печать | 7 |
| 8 | true | continue → пропуск | — |
| 9 | false | Печать | 9 |
| 10 | — | Выход из цикла | — |
Визуализация continue
for i := 1; i <= 5; i++ {
if i == 3 {
continue // Пропуск
}
fmt.Println(i)
}
Порядок:
- i=1 → печать
1 - i=2 → печать
2 - i=3 →
continue→ пропускfmt.Println(3) - i=4 → печать
4 - i=5 → печать
5
Вывод:
1
2
4
5
Опасность: бесконечный цикл с continue
Проблемный код
i := 0
for i < 10 {
if i%2 == 0 {
continue // ❌ ОШИБКА: i никогда не увеличивается!
}
fmt.Println(i)
i++
}
Что происходит:
- i=0
0%2 == 0→ truecontinue→ возврат к проверке условия- i=0 (не изменился!)
0%2 == 0→ truecontinue→ возврат к проверке условия- Бесконечный цикл
Отладка с дебаггером
Шаги:
- Поставить breakpoint на
for i < 10 - Запустить Debug
- Step Over (F10) → проверка условия
- Step Over → проверка
i%2 == 0→ true - Step Over →
continue - Step Over → снова проверка условия (i не изменился!)
- Повторение бесконечно
Вывод в терминале:
......................................
(точки печатаются бесконечно)
Правильный код
i := 0
for i < 10 {
i++ // ✅ Приращение ДО continue
if i%2 == 0 {
continue
}
fmt.Println(i)
}
Практический пример: игра “Угадай число”
Код
secretNumber := rand.Intn(10) + 1 // Загадано число 1-10
guesses := 0
for {
guesses++
guess := rand.Intn(10) + 1
fmt.Printf("Попытка %d: угадываем число %d... ", guesses, guess)
if guess == secretNumber {
fmt.Printf("✓ Правильно! (число было %d)\n", secretNumber)
break // Выход из цикла
} else {
fmt.Println("✗ Неправильно")
}
if guesses >= 5 {
fmt.Printf("✗ Не угадали за 5 попыток (было %d)\n", secretNumber)
break // Выход после 5 попыток
}
}
Пример вывода (успех)
Попытка 1: угадываем число 3... ✗ Неправильно
Попытка 2: угадываем число 7... ✗ Неправильно
Попытка 3: угадываем число 5... ✓ Правильно! (число было 5)
Пример вывода (неудача)
Попытка 1: угадываем число 2... ✗ Неправильно
Попытка 2: угадываем число 9... ✗ Неправильно
Попытка 3: угадываем число 4... ✗ Неправильно
Попытка 4: угадываем число 1... ✗ Неправильно
Попытка 5: угадываем число 8... ✗ Неправильно
✗ Не угадали за 5 попыток (было 7)
Анализ
Почему бесконечный цикл?
- Количество попыток заранее неизвестно
- Выход через
breakпри угадывании или превышении лимита
Два условия выхода:
guess == secretNumber→ успехguesses >= 5→ лимит попыток
Когда использовать каждую форму
Классический for (95-99% задач)
for i := 0; i < 10; i++ {
// Известное количество итераций
}
Применение:
- Обход массивов/слайсов по индексу
- Фиксированное количество повторений
- Итерации с шагом
For как while
for condition {
// Неизвестное количество итераций
}
Применение:
- Чтение файлов до конца
- Обработка пользовательского ввода
- Ожидание события
- Алгоритмы с неопределённым числом шагов
Бесконечный цикл
for {
// Работа до break
}
Применение:
- Игровые циклы
- Серверные приложения (обработка запросов)
- Мониторинг событий
- Интерактивные CLI-приложения
Рекомендации по использованию
✅ Хорошие практики
- Используйте классический for, если количество итераций известно
- Используйте for-while, если количество итераций зависит от условия
- Бесконечный цикл обязательно должен иметь условие выхода (
break) - Избегайте break/continue, если задача решается без них
- Приращение счётчика делайте ДО continue, иначе бесконечный цикл
❌ Плохие практики
- Не эмулируйте do-while — код становится нечитаемым
- Не используйте break/continue без необходимости — усложняет логику
- Не создавайте бесконечные циклы без выхода — программа зависнет
- Не размещайте приращение после continue — риск бесконечного цикла
Сравнение операторов break и continue
| Характеристика | break | continue |
|---|---|---|
| Действие | Выход из цикла | Переход к следующей итерации |
| Что пропускается | Весь оставшийся цикл | Остаток текущей итерации |
| Проверка условия | Нет (выход) | Да (перед новой итерацией) |
| Постусловие (post) | Нет | Да (выполняется перед проверкой) |
| Применение | Досрочное завершение | Пропуск особых случаев |
Пример различия
for i := 1; i <= 5; i++ {
if i == 3 {
break // Выход: печатает 1, 2
}
fmt.Println(i)
}
for i := 1; i <= 5; i++ {
if i == 3 {
continue // Пропуск: печатает 1, 2, 4, 5
}
fmt.Println(i)
}
Ключевые моменты
- Go имеет только один цикл —
for - For может работать как while:
for condition { } - Бесконечный цикл:
for { }с обязательнымbreak - Break: немедленный выход из цикла
- Continue: пропуск остатка итерации, переход к следующей
- Приращение ДО continue, иначе бесконечный цикл
- Используйте классический for в 95-99% случаев
- Бесконечный цикл требует условия выхода, иначе программа зависнет
- Избегайте break/continue без необходимости — усложняют код
- Дебаггер помогает понять порядок выполнения и найти бесконечные циклы