День 18 · map: таблица по ключу · страница 6 из 6

Трассировка с map и выбор: срез или map

Последняя страница дня: как map записывается в таблицу трассировки, когда выбирать map, а когда срез, и словарик.


Колонка map в таблице

Ситуация. В программе есть map, и её значение нужно вписать в таблицу трассировки клеткой, как число. Правила 1–29 дней 02, 06, 08, 11, 12, 13, 16 и 17 действуют. К ним три новых. Номера строк — слева:

Код для чтения · разбираем, набирать не нужно
 1  package main
 2
 3  import "fmt"
 4
 5  func add(m map[string]int, sku string) {
 6  	m[sku]++
 7  }
 8
 9  func main() {
10  	counts := map[string]int{}
11  	counts["T-118"]++
12  	counts["A-210"] += 3
13  	have := counts["P-305"]
14  	add(counts, "T-118")
15  	delete(counts, "A-210")
16  	fmt.Println(counts, have)
17  }

Вызов add — один шаг, шаги внутри функции не расписываем (правило 22).

шаг строка counts have
1 10 map[] -
2 11 map[T-118:1] -
3 12 map[A-210:3 T-118:1] -
4 13 map[A-210:3 T-118:1] 0
5 14 map[A-210:3 T-118:2] 0
6 15 map[T-118:2] 0
7 16 map[T-118:2] 0

Правила для map.

  1. Колонка map записывается так, как её печатает fmt.Println: map[ключ:значение ключ:значение] — пары через пробел, ключ и значение через двоеточие, ключи по возрастанию, строки без кавычек. Пустая map — map[]; map, которая ещё не объявлена, — прочерк -. На шаге 3 ключ A-210 добавлен вторым, а стоит первым: порядок в клетке — порядок ключей, а не порядок записи.
  2. Новый ключ появляется в колонке только при записи: m[k] = …, m[k]++, m[k] += … — даже если прибавили ноль. Чтение x := m[k] и x, ok := m[k] колонку map не меняет, даже когда ключа нет, — шаг 4. delete(m, k) убирает пару; delete ключа, которого нет, ничего не меняет.
  3. Map, переданная в функцию, — та же самая map: запись внутри функции меняет колонку map вызывающего. Если вызов — один шаг (правило 22), изменение видно уже в строке вызова — шаг 5. Если шаги функции расписаны, колонка вызывающего меняется в той строке функции, где сделана запись. Это исключение из правила 20: у переменных int и string вызывающего так не бывает.

Проверить таблицу можно отладочной печатью в копии программы (правило 6). После каждой строки main вставляется печать с номером строки исходной программы, например после строки 10 — fmt.Println("после строки 10:", counts), а после строк с 13-й — fmt.Println("после строки 13:", counts, have):

Вывод · это печатает программа, набирать не нужно
после строки 10: map[]
после строки 11: map[T-118:1]
после строки 12: map[A-210:3 T-118:1]
после строки 13: map[A-210:3 T-118:1] 0
после строки 14: map[A-210:3 T-118:2] 0
после строки 15: map[T-118:2] 0
map[T-118:2] 0

Последняя строка — печать самой программы, шаг 7.

Обход for k, v := range m по самой map по шагам не расписывают: его порядок не определён, и верного ответа у такой таблицы нет. Обход slices.Sorted(maps.Keys(m)) — обычный обход среза (правила дня 16).


Попробуйте сейчас: задание trace.

Цель: все ? в ~/gocourse/day18/trace/table.txt заменены значениями без запуска. Артикулы и числа у каждого свои.

1. Откройте программу и таблицу рядом:

▶ Выполните
cd ~/gocourse/day18/trace
cat program.go
cat table.txt

2. Заполните все ? по правилам 30–32. Артикулы — латиницей, как в program.go; колонка ok — true или false.

3. Сверьте клетки отладочной печатью в копии, program.go не меняйте:

▶ Выполните
cp program.go ~/gocourse/day18/scratch/main.go
cd ~/gocourse/day18/scratch
go run .

В копию после каждой строки main допишите fmt.Println(stock).

4. Закоммитьте:

▶ Выполните
cd ~/gocourse/day18
git add .
git commit -m "trace: таблица с map"

Готово, когда: в таблице не осталось ?, пункт trace зелёный.


Срез или map

Что нужно Срез map
порядок: строки ввода, очередь заказов хранит не хранит
найти по артикулу перебор сразу, по ключу
повторы одного значения хранит все один ключ — одна пара
объявить через var и сразу писать append к nil-срезу работает запись в nil-map — паника

Хранить по порядку — срез, считать и группировать — map. Часто вместе, как в задании group: строки ввода — в срезы, итоги — в map, для печати ключи снова становятся отсортированным срезом. Подробная таблица — в course extra 18.


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

Цель: убедиться, что работа дня записана.

▶ Выполните
cd ~/gocourse/day18
git status -s
git log --oneline

Готово, когда: git status -s ничего не печатает, а в git log --oneline не меньше четырёх строк — пункт git. Незакоммиченное — git add . и git commit -m "…".


Словарик ошибок

Допишите в ~/errors.md сообщения, которые встретили сегодня: текст ошибки, тире, что это значило в вашем коде.

Образец · набирать не нужно
panic: assignment to entry in nil map - объявил map через var и не создал
invalid operation: got != want (map can only be compared to nil) - сравнил две map через !=

Нужно не меньше 36 разборов за все дни. Разделитель — тире с пробелами. Кандидаты дня: invalid map key type, duplicate key … in map literal, undefined: maps, does not match iter.Seq[E], declared and not used: ok.


Итог дня

Что Как
создать m := map[string]int{}, make(map[string]int), литерал
запись, чтение m[k] = v, x := m[k]; нет ключа — нулевое значение
есть ли ключ, удалить v, ok := m[k], delete(m, k)
счётчик и сумма m[k]++, m[k] += q
по возрастанию for _, k := range slices.Sorted(maps.Keys(m))
nil-map читать можно, писать — паника
в функции и в тесте та же map; сравнение — maps.Equal

Завтра — строки изнутри: почему len("Болт") не 4, и чтение файла выгрузки.


Материал дня закончен. Задания — course lab 18, дополнительное чтение — course extra 18