День 13 · дополнительные материалы

День 13 — глубже

Всё здесь необязательно. Материалы на английском помечены.


Как называют функции в самом Go (англ.)

Спецификация: объявление функции (англ.)

Откуда взялось слово «рефакторинг» (англ.)

Git: история и просмотр (англ.)


Документация к Sprintf прямо в терминале

Попробуйте сейчас: чем Sprintf отличается от Printf.

Цель: увидеть в go doc, что одна функция печатает, а другая возвращает строку.

▶ Выполните
go doc fmt.Sprintf
go doc fmt.Printf

Прочитайте первую строку описания каждой: у Sprintf — «returns the resulting string», у Printf — «writes to standard output». Вот почему rubles со страницы 1 возвращает строку и ничего не печатает: печатать будет тот, кто её позвал.

Готово, когда: вы можете сказать, что вернёт fmt.Sprintf("%d", 5) и что напечатает fmt.Printf("%d", 5).


Что ещё умеет diff

diff сравнивает файлы, и у него есть формы вывода поудобнее обычной.

Пример · только посмотреть, набирать не нужно
stagiaire@lab:~/gocourse/day13/scratch$ diff -u baseline.txt after.txt
--- baseline.txt	2026-09-12 02:14:52.934255339 +0300
+++ after.txt	2026-09-12 02:15:41.118902744 +0300
@@ -1,3 +1,3 @@
 Штук: 50
 Коробок: 5
-Упаковка: 4.05 руб.
+Упаковка: 4.50 руб.
Ключ Что делает
-u показывает расхождение с соседними строками; так же выглядит git diff
-q только отвечает, различаются файлы или нет
-w не считает разницей количество пробелов

-u полезен, когда вывод длинный: видно, в каком месте вывода разошлось.

Где искать ошибку: сужаем по дереву вызовов

Программа из шести функций напечатала неверное число. Читать все шесть подряд — долго и обычно бесполезно. Дерево вызовов со страницы 3 позволяет делить поиск пополам, и это тот же приём, что в дне 09, только вместо строк вы делите функции.

Порядок такой:

  1. Начните с main и найдите строку, которая печатает неверное число. Из неё видно, чей результат врёт: rubles(cost) — либо cost, либо rubles.
  2. Проверьте функцию-лист отдельно. Позовите её в scratch с теми же числами и сравните с посчитанным на бумаге. Лист либо виноват, либо вычеркнут из поиска целиком — вместе со всеми вызовами.
  3. Если лист чист, поднимитесь на уровень выше. Виновата либо сама функция, которая его зовёт, либо аргументы, которые ей передали.
  4. Повторяйте, пока не останется одна функция. В ней уже читают построчно и, если нужно, ведут таблицу трассировки.

Пример на том же дереве со страницы 3. Упаковка напечаталась как 0.00, хотя коробок пять. В main за это отвечает rubles(cost) — под подозрением две функции из четырёх. Проверяем лист rubles на числе 405: печатает 4.05, значит, он в порядке и виноват cost. Его даёт packCost — осталась одна функция из четырёх, и в ней три строки.

Ровно для этого функции и возвращают результат, а не печатают сами: каждую можно позвать отдельно и вычеркнуть из списка подозреваемых одним сравнением. С программой из одного main так не выйдет — в ней нечего вычёркивать, кроме строк.



Мини-опыты

1. Порядок объявления функций. В scratch/main.go напишите main первой, а вызываемые функции — после неё. Запустите. Программа работает: порядок объявления функций в файле значения не имеет. Теперь так же переставьте две строки внутри main — результат изменится. Почему для функций порядок не важен, а для строк внутри функции важен?

2. Функция, которая ничего не возвращает. Наберите в scratch/main.go:

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

import "fmt"

func note(text string) {
	fmt.Println(text)
}

func main() {
	note("первый")
	x := note("привет")
	fmt.Println(x)
}
Вывод · это печатает программа, набирать не нужно
# day13/scratch
./main.go:11:7: note("привет") (no value) used as value

Функция без результата не годится в правой части присваивания: возвращать ей нечего. Уберите строки с x — останется программа, которая печатает одно слово.

3. Сколько строк экономит вынос. Посчитайте строки в split/main.go до и после разбиения — командой wc -l ~/gocourse/day13/split/main.go.

Скорее всего, файл стал длиннее: заголовки функций и return занимают место. Короче стал main — то, что читают в первую очередь. Разбиение экономит не строки, а внимание читающего.

4. Один кусок — два места. В split после разбиения вызовите boxesFor (или как вы её назвали) ещё раз — в отдельной программе в scratch, скопировав функцию туда. Сколько строк пришлось скопировать? Это цена того, что функция получает всё нужное аргументами и не лезет в пакетные переменные.