Трассировка
Программа с =+ собралась и напечатала 15 вместо 55 — компилятор тут не поможет. Чтобы найти такую ошибку, надо видеть, что лежит в ячейках после каждой строки. Этот приём называется трассировкой: вы выполняете программу в голове так же, как компьютер, и записываете значения в таблицу.
Как программу выполняет компьютер
У исполнителя три вещи: текущая строка, ячейки памяти с именами и значениями и экран. Работает он по одному правилу:
- Взять текущую строку.
- Выполнить её:
- объявление — создать ячейку и положить значение;
- присваивание — вычислить правую часть по тому, что сейчас лежит в ячейках, и записать результат в ячейку слева;
- печать — вывести значения на экран. Ячейки не меняются.
- Перейти к следующей строке.
Больше он ничего не умеет. sold для него не «продано», а просто ячейка. Прошлых значений он не помнит, вперёд не заглядывает и не догадывается, что задумал автор. Чтобы трассировать, станьте таким же исполнителем: пишите в таблицу то, что получается по коду, а не то, что должно получиться по смыслу.
Таблица трассировки
Ситуация. Нужно понять, какие значения будут после каждой строки. Номера строк — слева, как в VS Code:
1 package main
2
3 import "fmt"
4
5 func main() {
6 stock := 20
7 sold := 0
8 sold = sold + 3
9 stock = stock - sold
10 sold += 2
11 stock = stock - 2
12 fmt.Println(stock, sold)
13 }| шаг | строка | что происходит | stock | sold | экран |
|---|---|---|---|---|---|
| 1 | 6 | создать stock, положить 20 |
20 | - | |
| 2 | 7 | создать sold, положить 0 |
20 | 0 | |
| 3 | 8 | sold + 3 → 0 + 3 = 3 |
20 | 3 | |
| 4 | 9 | stock - sold → 20 − 3 = 17 |
17 | 3 | |
| 5 | 10 | sold + 2 → 3 + 2 = 5 |
17 | 5 | |
| 6 | 11 | stock - 2 → 17 − 2 = 15 |
15 | 5 | |
| 7 | 12 | напечатать stock и sold |
15 | 5 | 15 5 |
stagiaire@lab:~/gocourse/day02/scratch$ go run .
15 5Правила заполнения. Они одни на весь курс — для таблиц на страницах, в заданиях и на контрольных точках.
- Одна строка таблицы — одна выполненная строка кода внутри
main, включая строки с печатью. Печать ячейки не меняет: значения в её строке те же, что строкой выше (шаг 7). Пока шаги идут подряд по строкам; разойдутся в день 08, когда в циклах одна строка будет выполняться много раз. - Значения — после выполнения шага. Для
sold = sold + 3записано 3, а не 0. - Переменной ещё нет — прочерк
-. На шаге 1 ячейкиsoldнет. Это не ноль: ноль появится на шаге 2. - То, что шаг не трогал, переписать из строки выше. Пустая клетка не значит «там то же самое» — значение пишется в каждой строке.
- Правую часть считать по предыдущей строке таблицы.
- Номера строк — по исходному файлу программы, как он открывается в редакторе, без ваших вставок. Опыты с отладочной печатью — только в копии программы в
scratch: каждая вставка сдвигает номера строк.
Проверить таблицу запуском
Сначала предсказать, потом проверить. Промежуточные значения покажет отладочная печать — временные Println после каждой строки:
package main
import "fmt"
func main() {
stock := 20
fmt.Println("после строки 6:", stock)
sold := 0
fmt.Println("после строки 7:", stock, sold)
sold = sold + 3
fmt.Println("после строки 8:", stock, sold)
stock = stock - sold
fmt.Println("после строки 9:", stock, sold)
sold += 2
fmt.Println("после строки 10:", stock, sold)
stock = stock - 2
fmt.Println("после строки 11:", stock, sold)
fmt.Println(stock, sold)
}stagiaire@lab:~/gocourse/day02/scratch$ go run .
после строки 6: 20
после строки 7: 20 0
после строки 8: 20 3
после строки 9: 17 3
после строки 10: 17 5
после строки 11: 15 5
15 5В подписях — номера строк исходной программы: после вставок строки сдвинулись, и stock := 20 теперь на строке 6, а sold := 0 — на 8. Поэтому отладочную печать добавляют в копию программы в scratch, а исходный файл не трогают: номера в таблице остаются номерами строк в редакторе. После строки 6 печатается только stock: sold ещё не объявлена. Это же первый приём поиска ошибок — подробно в день 09.
Запуск не заменяет трассировку: на ревью чужого кода и на контрольной точке запустить нельзя.
Попробуйте сейчас: трассировка в черновике.
Цель: таблица на бумаге, сверенная с отладочной печатью.
- Наберите программу в
scratch/main.go:✎ Наберите в файлscratch/main.gopackage main import "fmt" func main() { orders := 5 boxes := orders orders += 3 boxes = boxes + orders orders-- fmt.Println(orders, boxes) }
- На бумаге заполните таблицу «шаг | строка | orders | boxes», не запуская.
- Потом добавьте отладочную печать, запустите и сравните:
▶ Выполнитеcd ~/gocourse/day02/scratch go run .Где разошлось — там ваше заблуждение о том, как работает программа.
Готово, когда: значения из отладочной печати совпали с таблицей на бумаге или вы нашли шаг, на котором они разошлись.
Таблица в файле
В заданиях trace1 и trace2 таблица — файл table.txt рядом с program.go. Формат файла тот же во всех днях курса:
- первая строка — шапка-комментарий:
#, потом названия колонок
шаг | строка | …, по колонке на каждую переменную;
- дальше по строке на каждую выполненную строку
main, печать тоже; - шаг, номер строки и прочерки
-уже вписаны, на месте значений —?; менять можно только?.
Для программы выше заполненный файл выглядел бы так:
# шаг | строка | stock | sold
1 | 6 | 20 | -
2 | 7 | 20 | 0
3 | 8 | 20 | 3
4 | 9 | 17 | 3
5 | 10 | 17 | 5
6 | 11 | 15 | 5
7 | 12 | 15 | 5Проверка сравнивает с эталоном только значения и порядок строк.
Выравнивание, число пробелов, пробелы вокруг |, пустые строки и строки с # не важны. Где именно ошибка, проверка не говорит — иначе таблицу можно было бы подобрать перебором. Она скажет одно из трёх: остались ?, число строк не то или какое-то значение не совпало.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
В таблице ещё есть строки со знаком ?: 2 |
заполнены не все значения | найти ?: в VS Code Ctrl+F |
Строк с шагами в таблице 7, а нужно 8 |
строку удалили, добавили или закомментировали | переименовать table.txt, выполнить course lab 02 — появится новая заготовка, перенести в неё значения |
Все ? заполнены, но где-то значение не совпало |
ошибка в каком-то шаге | пройти заново по правилам, сверить отладочной печатью в копии в scratch |
| на шаге с печатью значения другие, чем строкой выше | решили, что печать что-то меняет | Println только читает |
| номера строк в редакторе не совпадают с таблицей | в program.go добавили строки |
убрать вставки из program.go; опыты — в копии в scratch |
вместо - стоит 0 или длинное тире — |
прочерк заменили | обычный дефис-минус - |
Попробуйте сейчас: задание
trace1.Цель: в
trace1/table.txtвсе?заменены значениями, найденными без запуска.Задание
trace1— резерв товара под заказы, числа у каждого свои.
- Перейдите в папку задания и посмотрите, что в ней:
▶ Выполнитеcd ~/gocourse/day02/trace1 ls
- Откройте
program.goиtable.txtрядом: номера строк в таблице — это номера строкprogram.goв редакторе. Заполните все?без запуска.- Потом запустите — последние значения должны совпасть с последней строкой таблицы:
▶ Выполнитеgo run .Файл называется не
main.go, ноgo run .собирает все.go-файлы каталога, имя значения не имеет.
- Сверить промежуточные значения отладочной печатью — в копии,
program.goне меняйте. Копия заменит то, что лежит в черновике:▶ Выполнитеcp program.go ~/gocourse/day02/scratch/main.go cd ~/gocourse/day02/scratch go run .В подписях отладочной печати пишите номера строк из
program.go: в копии они сдвинутся.Готово, когда: в
table.txtне осталось ни одного?, последняя строка совпала с выводомgo run .— пункт проверкиtrace1.
Дальше: что на самом деле делает b := a и как поменять два значения местами — course next