День 08 · Циклы: for, границы, накопители · страница 1 из 7

Цикл for: повторить тело несколько раз

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


Три части заголовка

Ситуация. Для партии из трёх коробок нужно напечатать три этикетки.

Код для чтения · разбираем, набирать не нужно
package main

import "fmt"

func main() {
	for i := 0; i < 3; i++ {
		fmt.Println("Этикетка номер", i)
	}
	fmt.Println("Этикетки готовы")
}

Что увидите.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day08/scratch$ go run .
Этикетка номер 0
Этикетка номер 1
Этикетка номер 2
Этикетки готовы

Как это читать. Строка с for — заголовок: три части через точку с запятой. В фигурных скобках — тело, то, что повторяется.

Кусок Когда выполняется Что делает
i := 0 один раз, до всего создаёт переменную цикла — счётчик
i < 3 перед каждым проходом истинно — тело, ложно — выход из цикла
i++ после каждого прохода плюс единица к счётчику
{ … } пока условие истинно тело цикла

Порядок такой: i := 0; проверка истинна — тело, потом i++ и проверка снова; так трижды, при i = 0, 1, 2; после третьего i++ в i уже 3, проверка 3 < 3 ложна, и программа идёт к строке после }. Условие проверено четыре раза, тело выполнилось три: последняя проверка всегда ложная, иначе цикл бы не кончился. Один проход тела — итерация.

Круглых скобок вокруг заголовка нет, фигурные обязательны, { — в конце строки с for. Имя i — традиция, годится любое.


Попробуйте сейчас: этикетки в цикле.

Цель: своя программа с циклом, у которой число проходов задаёт переменная.

1. Наберите в scratch/main.go программу из раздела «Три части заголовка»:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	for i := 0; i < 3; i++ {
		fmt.Println("Этикетка номер", i)
	}
	fmt.Println("Этикетки готовы")
}

Запустите:

▶ Выполните
cd ~/gocourse/day08/scratch
go run .

2. Поменяйте 3 на 5: этикеток пять, а Println одна.

3. Объявите до цикла n := 4, условие — i < n: число проходов задаёт переменная, а её можно будет прочитать из ввода. Запустите.

Готово, когда: печатается Этикетка номер 0…3 и Этикетки готовы, а в заголовке стоит i < n.


Переменная цикла живёт внутри

Ситуация. После этикеток нужно напечатать, сколько коробок в партии. Кажется, это последнее значение i.

Дальше показаны только строки тела main: вставляйте их между { и } в scratch/main.go. С одним import "fmt" первая строка тела — шестая.

Код для чтения · разбираем, набирать не нужно
	for i := 0; i < 3; i++ {
		fmt.Println("Коробка", i)
	}
	fmt.Println("Всего коробок:", i)
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day08/scratch$ go run .
# day08/scratch
./main.go:9:44: undefined: i

i объявлена в заголовке и живёт до закрывающей } цикла — как переменная внутри if в день 06: на строке 9 её уже нет (колонка 44 — в байтах). Нужно значение после цикла — переменную объявляют до него, а в теле меняют:

Код для чтения · разбираем, набирать не нужно
	count := 0
	for i := 0; i < 3; i++ {
		fmt.Println("Коробка", i)
		count++
	}
	fmt.Println("Всего коробок:", count)
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day08/scratch$ go run .
Коробка 0
Коробка 1
Коробка 2
Всего коробок: 3

count — счётчик: до цикла ноль, в каждом проходе плюс один (о таких переменных — вся страница 6). Объявленная в теле переменная создаётся заново на каждом проходе, и правило «объявил — используй» для неё то же:

Код для чтения · разбираем, набирать не нужно
	for day := 1; day <= 3; day++ {
		sold := day * 10
	}
	fmt.Println("готово")
Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day08/scratch$ go run .
# day08/scratch
./main.go:7:3: declared and not used: sold

Попробуйте сейчас: переменная цикла после цикла.

Цель: увидеть undefined: i, починить счётчиком, записать в словарик.

1. Наберите в scratch/main.go вариант с i после цикла:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	for i := 0; i < 3; i++ {
		fmt.Println("Коробка", i)
	}
	fmt.Println("Всего коробок:", i)
}
▶ Выполните
cd ~/gocourse/day08/scratch
go run .

Найдите в сообщении строку и имя.

2. Почините через счётчик count и запустите снова.

3. Запишите undefined: i в ~/errors.md: текст ошибки, тире, что это значило.

Готово, когда: печатается Всего коробок: 3, а в ~/errors.md есть строка про undefined: i — она идёт в пункт errors.


Что может пойти не так

Номер строки и колонки в сообщениях свой, текст — тот же.

Что видите Что это значит Что делать
undefined: i переменную цикла читают после цикла объявить до цикла, менять в теле
declared and not used: sold переменную в теле объявили и не читают использовать или удалить
syntax error со словами after for clause или expected ) заголовок не по-гошному: круглые скобки, запятые вместо ;, { на своей строке таблица чужого синтаксиса в course extra 08
после Ctrl+S скобки в for (n > 0) { исчезли вокруг одного условия скобки допустимы, gofmt убирает их как лишние так и оставить

Попробуйте сейчас: что напечатает программа.

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

1. Сначала не запуская: выпишите на бумаге i и n после каждой проверки условия.

Код для чтения · разбираем, набирать не нужно
	n := 5
	for i := 0; i < n; i++ {
		n--
	}
	fmt.Println(n)

Условие проверяется перед каждым проходом, и n в нём — то, что лежит там сейчас.

2. Сдать число (вместо ЧИСЛО — своё предсказание):

▶ Выполните · выделенное замените своим
course answer day08.q1 ЧИСЛО

3. Потом проверьте запуском — программа целиком:

✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	n := 5
	for i := 0; i < n; i++ {
		n--
	}
	fmt.Println(n)
}
▶ Выполните
cd ~/gocourse/day08/scratch
go run .

Готово, когда: пункт q1 в course check 08 зелёный.


Дальше: сколько раз выполнится тело и ошибка на единицу — course next