Урок курса
Цикл for: три формы, break и continue
Golang с нуляВ прошлом уроке мы разбирали if и switch — инструменты для ветвления. Теперь добавим повторение: for в Go — единственный цикл, но он достаточно гибок, чтобы заменить и while, и бесконечный цикл с флагом выхода.
Три формы цикла for: синтаксис и механизм выполнения
Go не имеет while или do-while — только for. Зато у него три формы записи, и каждая уместна в своей ситуации.
Форма с инициализатором, условием и постшагом — то, что в других языках называют классическим for:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Порядок выполнения фиксирован: сначала один раз выполняется инициализатор i := 0, затем перед каждой итерацией проверяется условие i < 5, и только если оно истинно — выполняется тело. После тела — постшаг i++, и снова проверка условия. Когда условие становится ложным, цикл завершается.
Важная деталь: переменная i, объявленная через := в инициализаторе, существует только внутри цикла. За его пределами она недоступна. Это не ограничение, а разумная область видимости — счётчик цикла редко нужен после его завершения.
Ещё одна особенность Go: i++ — это инструкция, а не выражение. Написать j := i++ не получится — компилятор откажет. Это сделано намеренно, чтобы устранить целый класс ошибок вроде a[i++] = i++.
Форма только с условием — аналог while:
n := 1
for n < 100 {
n *= 2
}
fmt.Println(n) // 128
Здесь нет инициализатора и постшага — только условие. Переменная n объявлена до цикла, потому что её значение нужно после выхода.
Бесконечный цикл — форма вообще без условия:
for {
// выполняется бесконечно
}
Выйти из него можно только явно: через break, return или в экстремальных случаях os.Exit. Именно эта форма удобна, когда условие выхода проверяется не в заголовке цикла, а где-то в середине тела — что и понадобится в итоговом примере урока.
Во всех трёх формах тело цикла обязательно заключено в фигурные скобки — без исключений, как и в if.
break и continue: досрочный выход и пропуск итерации
break и continue управляют ходом выполнения цикла, но делают это по-разному.
break немедленно завершает ближайшую охватывающую конструкцию — это может быть for, switch или select. Важно: если break стоит внутри switch, вложенного в цикл, он завершит switch, а не цикл. Мы разбирали switch в прошлом уроке — теперь это различие становится практически значимым.
continue, в отличие от break, относится только к циклу — к ближайшему охватывающему for. Он прерывает текущую итерацию и переходит к следующей. Но «следующая итерация» — это не просто начало тела. В форме с постшагом for i := 0; i < n; i++ после continue сначала выполняется i++, и только затем проверяется условие. Это принципиально.
for i := 0; i < 5; i++ {
if i == 2 {
continue
}
fmt.Println(i)
}
Вывод:
0 1 3 4
Когда i равно 2, continue пропускает fmt.Println, но i++ всё равно выполняется — поэтому следующая итерация начнётся с i = 3, а не зависнет на i = 2. Цикл не стал бесконечным.
for i := 0; i < 5; i++ {
if i == 3 {
break
}
fmt.Println(i)
}
Вывод:
0 1 2
Как только i достигает 3, цикл завершается полностью — постшаг не выполняется, условие не проверяется.
Если циклы вложены, оба оператора без метки действуют на внутренний цикл — тот, в теле которого они стоят. Внешний цикл продолжается. Это отдельно от ситуации со switch: break внутри switch всегда завершает switch, даже если снаружи есть цикл — на цикл такой break не влияет.
Бесконечный цикл с break: суммирование чисел до ввода 0
Соберём всё вместе в задаче: читать числа с клавиатуры и накапливать их сумму, пока пользователь не введёт 0.
package main
import "fmt"
func main() {
var sum, n int
for {
fmt.Scan(&n)
if n == 0 {
break
}
sum += n
}
fmt.Println(sum)
}
Пример работы:
3 7 -2 0 8
Обе переменные объявлены до цикла через var sum, n int, но по разным причинам.
sum — накопитель, и его объявление снаружи обязательно по двум причинам. Первая: значение должно сохраняться между итерациями. Если написать sum := 0 внутри тела цикла, на каждой итерации создаётся новая переменная с нулевым начальным значением — всё накопленное за предыдущие итерации теряется. Вторая: sum нужна после завершения цикла — в строке fmt.Println(sum). Переменная, объявленная через := внутри тела for, недоступна за его пределами; программа с sum внутри цикла попросту не скомпилировалась бы.
n — другой случай. fmt.Scan(&n) принимает адрес переменной, но само по себе это не требует объявлять n вне цикла — var n int или n := 0 внутри тела тоже допустимы синтаксически. Внешнее объявление здесь — вопрос удобства: переменная повторно используется на каждой итерации без лишних объявлений.
Почему выбрана бесконечная форма for, а не for n != 0? Потому что условие выхода проверяется уже после чтения нового значения n, то есть в середине тела. Форма только с условием потребовала бы либо читать первое число до цикла, либо дублировать логику. Бесконечный for с break внутри выражает намерение прямее: «читай до тех пор, пока сам не решишь остановиться».
Попробуйте решить
Что выведет следующая программа?
package main
import "fmt"
func main() {
sum := 0
for i := 1; i <= 5; i++ {
if i == 3 {
continue
}
sum += i
}
fmt.Println(sum)
}
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
