День 02 · Переменные и присваивание · страница 4 из 6

Трассировка

Программа с =+ собралась и напечатала 15 вместо 55 — компилятор тут не поможет. Чтобы найти такую ошибку, надо видеть, что лежит в ячейках после каждой строки. Этот приём называется трассировкой: вы выполняете программу в голове так же, как компьютер, и записываете значения в таблицу.


Как программу выполняет компьютер

У исполнителя три вещи: текущая строка, ячейки памяти с именами и значениями и экран. Работает он по одному правилу:

  1. Взять текущую строку.
  2. Выполнить её:
    • объявление — создать ячейку и положить значение;
    • присваивание — вычислить правую часть по тому, что сейчас лежит в ячейках, и записать результат в ячейку слева;
    • печать — вывести значения на экран. Ячейки не меняются.
  3. Перейти к следующей строке.

Больше он ничего не умеет. 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

Правила заполнения. Они одни на весь курс — для таблиц на страницах, в заданиях и на контрольных точках.

  1. Одна строка таблицы — одна выполненная строка кода внутри main, включая строки с печатью. Печать ячейки не меняет: значения в её строке те же, что строкой выше (шаг 7). Пока шаги идут подряд по строкам; разойдутся в день 08, когда в циклах одна строка будет выполняться много раз.
  2. Значения — после выполнения шага. Для sold = sold + 3 записано 3, а не 0.
  3. Переменной ещё нет — прочерк -. На шаге 1 ячейки sold нет. Это не ноль: ноль появится на шаге 2.
  4. То, что шаг не трогал, переписать из строки выше. Пустая клетка не значит «там то же самое» — значение пишется в каждой строке.
  5. Правую часть считать по предыдущей строке таблицы.
  6. Номера строк — по исходному файлу программы, как он открывается в редакторе, без ваших вставок. Опыты с отладочной печатью — только в копии программы в 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.

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


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

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

  1. Наберите программу в scratch/main.go:
✎ Наберите в файл scratch/main.go
package main

import "fmt"

func main() {
	orders := 5
	boxes := orders
	orders += 3
	boxes = boxes + orders
	orders--
	fmt.Println(orders, boxes)
}
  1. На бумаге заполните таблицу «шаг | строка | orders | boxes», не запуская.
  2. Потом добавьте отладочную печать, запустите и сравните:
▶ Выполните
cd ~/gocourse/day02/scratch
go run .

Где разошлось — там ваше заблуждение о том, как работает программа.

Готово, когда: значения из отладочной печати совпали с таблицей на бумаге или вы нашли шаг, на котором они разошлись.


Таблица в файле

В заданиях trace1 и trace2 таблица — файл table.txt рядом с program.go. Формат файла тот же во всех днях курса:

шаг | строка | …, по колонке на каждую переменную;

Для программы выше заполненный файл выглядел бы так:

Образец · набирать не нужно
# шаг | строка | 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 — резерв товара под заказы, числа у каждого свои.

  1. Перейдите в папку задания и посмотрите, что в ней:
▶ Выполните
cd ~/gocourse/day02/trace1
ls
  1. Откройте program.go и table.txt рядом: номера строк в таблице — это номера строк program.go в редакторе. Заполните все ? без запуска.
  2. Потом запустите — последние значения должны совпасть с последней строкой таблицы:
▶ Выполните
go run .

Файл называется не main.go, но go run . собирает все .go-файлы каталога, имя значения не имеет.

  1. Сверить промежуточные значения отладочной печатью — в копии, 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