Трассировка: колонка на каждую переменную
Затенение тем и коварно, что в коде обе переменные выглядят одинаково. В таблице трассировки — не выглядят: у каждой своя колонка, и видно, что одна меняется, а другая стоит. Сегодняшняя таблица про это.
Два правила к прежним
Правила 1–20 из дней 02, 06, 08 и 11 действуют: строка if — шаг, строка с одной закрывающей } — не шаг, строка for — шаг при каждой проверке условия, после блока переменной блока нет (правила 1–15). День 11 добавил правила про вызов функции: колонка «где мы» (16), строка с вызовом встречается в таблице дважды (17), первый шаг внутри функции — строка заголовка (18), строка return — шаг (19), переменные другой функции — прочерк (20). Сегодня к ним два. Перечислять их тут приходится потому, что правила копились пять дней; целиком список лежит в памятке блока 3 — course page 15 2, раздел «Трассировка: правила 1–24».
21. Одноимённые переменные — разные колонки. Если внутри блока объявлена переменная с тем же именем, что снаружи, в таблице это две колонки. В заголовке пишется, где объявлена: trips и trips в if. Пока внутренняя не объявлена — прочерк; после закрывающей } её блока — снова прочерк, и колонка до конца таблицы пустая.
22. Строка с вызовом функции — один шаг, если шаги внутри функции не расписываются. В таблицу идут значения переменных того, кто вызвал, уже после возврата: результаты присвоены. Это сокращение — когда нужно разобраться, что происходит внутри, таблицу ведут по правилам дня 11, с колонкой «где мы».
Пример
Номера строк слева, как в VS Code:
1 package main
2
3 import "fmt"
4
5 func half(n int) int {
6 return n / 2
7 }
8
9 func main() {
10 stock := 30
11 part := half(stock)
12 if part > 10 {
13 part := part - 5
14 fmt.Println("внутри:", part)
15 }
16 fmt.Println("снаружи:", part)
17 }| шаг | строка | stock | part | part в if |
|---|---|---|---|---|
| 1 | 10 | 30 | - | - |
| 2 | 11 | 30 | 15 | - |
| 3 | 12 | 30 | 15 | - |
| 4 | 13 | 30 | 15 | 10 |
| 5 | 14 | 30 | 15 | 10 |
| 6 | 16 | 30 | 15 | - |
stagiaire@lab:~/gocourse/day12/scratch$ go run .
внутри: 10
снаружи: 15Разбор по шагам:
- Строка 10 создаёт
stock. Переменнойpartещё нет — прочерк. - Строка 11 — вызов
half. По правилу 22 это один шаг: внутрь не идём, записываем результат, который вернулся, — 15. - Строка 12 —
if: условие вычисляется, значения не меняются. Шаг всё равно есть (правило 7 дня 06). - Строка 13 —
part := part - 5. Появляется внутренняяpart: справа стоит внешняя, 15, значит внутри 10. Колонкаpartостаётся 15. - Строка 14 — печать внутренней: 10.
- Строка 15 — одна закрывающая
}, не шаг. Строка 16 печатает внешнююpart— 15, а внутренней уже нет: прочерк.
Последние две строки таблицы — то, что видно в выводе: внутри: 10 и снаружи: 15. Если бы в строке 13 стояло part = part - 5, колонка part в if осталась бы пустой всю таблицу, а в строке 16 напечаталось бы 10.
Попробуйте сейчас: таблица на бумаге.
Цель: заполнить таблицу для программы из примера, не подсматривая в готовую, и сверить с запуском.
1. Закройте таблицу выше рукой и заполните её сами — шесть шагов, три колонки со значениями.
2. Наберите ту же программу — уже без номеров строк — в
scratch/main.go:✎ Наберите в файлscratch/main.gopackage main import "fmt" func half(n int) int { return n / 2 } func main() { stock := 30 part := half(stock) if part > 10 { part := part - 5 fmt.Println("внутри:", part) } fmt.Println("снаружи:", part) }Запустите:
▶ Выполнитеcd ~/gocourse/day12/scratch go run .3. Поменяйте в строке 13
:=на=и запустите снова. Какая строка вывода изменилась и почему?Готово, когда: ваша таблица совпала с таблицей выше, а после замены
:=на=вторая строка вывода сталаснаружи: 10.
Проверить таблицу запуском
Отладочная печать из дня 09 работает и здесь — только печатать нужно в двух местах, до блока и внутри него, и подписывать, какая это переменная:
package main
import "fmt"
func half(n int) int {
return n / 2
}
func main() {
stock := 30
part := half(stock)
fmt.Println("до блока: part", part)
if part > 10 {
part := part - 5
fmt.Println("в блоке: part", part)
}
fmt.Println("после блока: part", part)
}stagiaire@lab:~/gocourse/day12/scratch$ go run .
до блока: part 15
в блоке: part 10
после блока: part 15Три строки читаются как три колонки таблицы: до блока внешняя part равна 15, в блоке напечатана внутренняя — 10, после блока снова внешняя, и она не изменилась. Если бы переменная была одна, вторая и третья строки были бы одинаковыми: это и есть способ отличить затенение от обычного присваивания, не читая код глазами.
Заметьте, что печать сдвинула номера строк: в этой программе if стоит в строке 13, а в исходной — в 12. Правило то же, что в днях 02 и 08: опыты — в копии, а номера строк в таблице — по исходному файлу. Копию удобно сделать одной командой:
stagiaire@lab:~/gocourse/day12/trace$ cp program.go ~/gocourse/day12/scratch/main.go
stagiaire@lab:~/gocourse/day12/trace$ cd ~/gocourse/day12/scratch
stagiaire@lab:~/gocourse/day12/scratch$ go run .Чего не видно в выводе
Печать показывает значения только там, где стоит Println. Таблица показывает их после каждой выполненной строки — и в этом её польза при разборе дефекта.
| Что | Видно в выводе | Видно в таблице |
|---|---|---|
| значение после каждой строки | только там, где есть печать | всегда |
| момент, когда появилась вторая переменная | никогда | строка с := внутри блока |
| момент, когда внутренняя переменная исчезла | никогда | прочерк после закрывающей } |
что условие if вообще проверялось |
только если ветка сработала | отдельный шаг на строке if |
| что внешняя переменная не менялась | если её печатают до и после | колонка стоит на месте всю таблицу |
Последняя строка — про сегодняшний дефект. Когда в колонке trips восемь одинаковых чисел, а в соседней колонке числа меняются, вопрос «почему после блока старое значение» отпадает сам собой.
Поэтому таблицу заполняют до запуска: она предсказание, а запуск — проверка предсказания. Если заполнять после, останется только переписать то, что и так напечаталось.
Таблица в файле
Задание trace — в том же формате, что в днях 02, 06, 08 и 11: файл
table.txt рядом с program.go, шапка-комментарий # шаг | строка | …, по строке на каждую выполненную строку main. Номера строк уже проставлены — считать нужно значения; прочерки и число строк не трогайте, меняйте только ?.
Колонок пять: stock, load, trips, rest и trips в if. Последняя — про переменную, объявленную внутри блока if (правило 21). Строка с вызовом ship — один шаг (правило 22).
Проверка сравнивает значения и порядок; пробелы и выравнивание не важны, регистр тоже. Где именно ошибка, она не говорит: это и есть задание.
Последняя строка таблицы проверяется запуском — числа в ней те же, что программа печатает.
Что может пойти не так
| Что видите | Что это значит | Что делать |
|---|---|---|
В таблице ещё осталось знаков ?: 3 |
заполнено не всё | найти ?: в VS Code Ctrl+F |
Строк с шагами в таблице 7, а нужно 8 |
строку удалили или добавили | переименовать table.txt, выполнить course lab 12 — появится новая заготовка |
Все ? заполнены, но где-то значение не совпало |
ошибка в каком-то шаге | пройти заново по правилам, сверить печатью в копии |
в колонке trips в if стоят числа с самого первого шага |
внутренняя переменная появляется только на своей строке | до неё — прочерк, он уже стоит в заготовке |
после строки } в колонке trips в if снова число |
переменная блока живёт до закрывающей } |
прочерк |
колонка trips меняется на шаге, где меняется trips в if |
это разные ячейки | внешняя меняется только на строках без := внутри блока |
| в последней строке таблицы не те числа, что печатает программа | где-то раньше пошло не так | сверить печатью значений по шагам |
Попробуйте сейчас: задание trace.
Цель: все
?вtrace/table.txtзаменены значениями, посчитанными без запуска. Числа у каждого свои.1. Откройте задание:
▶ Выполнитеcd ~/gocourse/day12/trace ls2. Откройте
program.goиtable.txtрядом. Заполните все?без запуска: восемь шагов, номера строк уже стоят.3. Потом запустите программу и сверьте последнюю строку таблицы с тем, что напечаталось:
▶ Выполнитеgo run .4. Промежуточные значения сверяйте в копии,
program.goне меняйте:▶ Выполнитеcp program.go ~/gocourse/day12/scratch/main.go cd ~/gocourse/day12/scratch go run .5. Таблица заполнена — закоммитьте:
▶ Выполнитеcd ~/gocourse/day12 git add . git commit -m "trace: таблица заполнена"Готово, когда: в таблице не осталось
?, числа последней строки совпали с выводом программы, пунктtraceвcourse check 12зелёный.
Дальше: программа целиком, коммиты и словарик — course next