Границы: сколько раз выполнится тело
Цикл, который делает на проход больше или меньше, собирается и работает: компилятор такой ошибки не видит. Значит, считать проходы придётся самому.
Четыре заголовка — четыре ответа
Ситуация. В условии «ячейки с 1 по n». Сколько проходов даст цикл, зависит от двух мелочей: с чего начать и какой знак.
n := 3
for i := 0; i < n; i++ {
fmt.Print(i, " ")
}
fmt.Println("— i := 0; i < n")
for i := 1; i <= n; i++ {
fmt.Print(i, " ")
}
fmt.Println("— i := 1; i <= n")
for i := 1; i < n; i++ {
fmt.Print(i, " ")
}
fmt.Println("— i := 1; i < n")
for i := 0; i <= n; i++ {
fmt.Print(i, " ")
}
fmt.Println("— i := 0; i <= n")Что увидите.
stagiaire@lab:~/gocourse/day08/scratch$ go run .
0 1 2 — i := 0; i < n
1 2 3 — i := 1; i <= n
1 2 — i := 1; i < n
0 1 2 3 — i := 0; i <= nfmt.Print(i, " ") печатает число и пробел, не переводя строку; переводит его Println после цикла.
Как это читать.
| Заголовок | Значения i |
Сколько проходов |
|---|---|---|
i := 0; i < n |
от 0 до n − 1 | n |
i := 1; i <= n |
от 1 до n | n |
i := 1; i < n |
от 1 до n − 1 | n − 1 |
i := 0; i <= n |
от 0 до n | n + 1 |
Со знаком < последнее значение не входит, со <= — входит. Отсюда подсчёт: для < проходов «конец минус начало», для <= — на один больше. Первые две формы дают ровно n проходов: «с нуля и <» считает позиции, «с единицы и <=» — номера, как у людей. Смешанные формы — обычно описка. Та же программа с n := 5 — в course extra 08.
Ошибка на единицу
Ячейки с 101 по 105 — это не 105 − 101 = 4 ячейки, а пять: как столбов у забора, где первый на нуле.
first := 101
last := 105
for cell := first; cell <= last; cell++ {
fmt.Println("Ячейка", cell)
}
fmt.Println("Ячеек:", last-first+1)stagiaire@lab:~/gocourse/day08/scratch$ go run .
Ячейка 101
Ячейка 102
Ячейка 103
Ячейка 104
Ячейка 105
Ячеек: 5Промах на один проход называют ошибкой на единицу (off-by-one): самый частый дефект в циклах. Ловят его тремя вопросами к заголовку:
- Какое первое значение получит переменная?
- Какое последнее пройдёт проверку?
- Сколько проходов всего — сходится ли с условием задачи?
И запуском на крайних данных: одна ячейка (first равно last), ни одной, диапазон на границе.
Попробуйте сейчас: починить границу в fix1.
Цель:
fix1печатает все ячейки; правка — одна-две строки.Он работает, но теряет одну ячейку; номера ячеек у каждого свои.
1. Запустите:
▶ Выполнитеcd ~/gocourse/day08/fix1 go run .2. Сравните вывод с комментарием в начале файла и задайте заголовку три вопроса выше. Больше двух изменённых строк проверка не примет;
firstиlastне меняйте: чинится граница, а не данные, и «Ячейка» должна печататься одной строкой — той, что внутри цикла.3. Посмотрите свою правку:
▶ Выполнитеdiff ~/.course/orig/day08/fix1/main.go main.goГотово, когда: вывод идёт от
firstдоlastвключительно и кончается строкой «Всего ячеек: …», аdiffпоказывает одну-две строки — пунктыfix1иt_diff.
Шаг и обратный отсчёт
Третья часть не обязана прибавлять единицу: доставка раз в неделю — шаг семь, отсчёт до отправки машин — шаг назад.
for day := 1; day <= 30; day += 7 {
fmt.Print(day, " ")
}
fmt.Println()
for left := 3; left > 0; left-- {
fmt.Println("До отправки машин:", left)
}
fmt.Println("Отправка")stagiaire@lab:~/gocourse/day08/scratch$ go run .
1 8 15 22 29
До отправки машин: 3
До отправки машин: 2
До отправки машин: 1
Отправкаfmt.Println() без аргументов только переводит строку. С шагом 7 число 30 не встретилось: после 29 идёт 36, и цикл кончился.
Бывает и так, что тело не выполняется ни разу: у for i := 5; i < 3; i++ первая же проверка ложна. Это не ошибка — пустой диапазон нормальный случай, и на нём программа должна вести себя разумно.
Попробуйте сейчас: сколько раз выполнится тело.
Цель: посчитать проходы цикла с шагом назад.
1. Сколько раз выполнится тело?
Код для чтения · разбираем, набирать не нужноcount := 0 for i := 10; i >= 0; i -= 2 { count++ } fmt.Println(count)Выпишите значения
i, при которых условие истинно, — с первым и последним.2. Сдать число (вместо
ЧИСЛО— сколько раз выполнится тело):▶ Выполните · выделенное замените своимcourse answer day08.q2 ЧИСЛО3. Потом проверьте запуском — программа целиком:
✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { count := 0 for i := 10; i >= 0; i -= 2 { count++ } fmt.Println(count) }▶ Выполнитеcd ~/gocourse/day08/scratch go run .Готово, когда: пункт
q2вcourse check 08зелёный.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
| не хватает последней строки или есть лишняя | ошибка на единицу: < вместо <=, начало с 0 вместо 1 |
три вопроса к заголовку |
| итог «Всего» на один меньше или больше | счётчик прошёл столько раз, сколько тело | чинить заголовок, а не итог |
| последнее значение с шагом не то | шаг перепрыгнул границу: 29 + 7 > 30 | проверить, какое значение последним проходит проверку |
Попробуйте сейчас: задание pack — таблица упаковки.
Цель:
pack/main.goпечатает таблицу упаковки по условиюTASK.txt.1. Прочитайте условие:
▶ Выполнитеcd ~/gocourse/day08/pack cat TASK.txt2. Напишите программу в
main.go: три числа — тремя вызовамиscanner.Scan(), как в день 07, таблица — один цикл от «от» до «до» включительно. Прежде чем писать заголовок, ответьте на три вопроса.3. Проверить себя:
▶ Выполнитеprintf '6\n10\n13\n' | go run .4. Потом неудобные вводы: «от» равно «до», «от» больше «до», партия ровно на целые коробки. Проверка прогонит и скрытые.
Готово, когда: на вводе 6, 10, 13 вывод совпадает с примером в
TASK.txt, аgofmt -l .молчит — пунктыpackиcode_fmt(второй — вместе сtotal).
Дальше: цикл с условием, break и continue — course next