День 18 — глубже
Всё здесь необязательно. Материалы на английском помечены.
Новая map внутри функции
На пятой странице запись в переданную map была видна снаружи. А если внутри функции присвоить параметру новую map?
package main
import "fmt"
func fill(counts map[string]int) {
counts = map[string]int{}
counts["K-417"] = 1
}
func main() {
var counts map[string]int
fill(counts)
fmt.Println(len(counts))
}0Присваивание меняет только копию указания внутри fill: новая map создана, запись ушла в неё, а переменная counts в main по-прежнему nil. Хотите, чтобы функция создала map, — возвращайте её результатом, как totals в задании group. С append внутри функции в дне 17 было то же самое.
Как сортируются строки
slices.Sorted сравнивает строки по байтам: цифры раньше латинских заглавных, заглавные раньше строчных, латиница раньше кириллицы. Для артикулов одного вида (A-210, K-417, M-120) это обычный алфавитный порядок. Но B-10 окажется раньше B-9: сравнение идёт по символам слева, и 1 меньше 9. Поэтому номера в артикулах делают одной длины: B-009, B-010.
Срез или map: подробно
| Что нужно | Срез | map |
|---|---|---|
| порядок значений: строки ввода, очередь заказов | хранит порядок | порядка нет |
| найти значение по артикулу | перебор всех элементов | сразу, по ключу |
| одно и то же значение несколько раз | хранит все повторы | один ключ — одна пара |
| номер элемента | есть | нет |
сравнить через == |
нельзя, только с nil |
нельзя, только с nil; целиком — maps.Equal |
| печать в одном и том же порядке | как лежит | через отсортированные ключи |
объявить через var и сразу писать |
append к nil-срезу работает |
запись в nil-map — паника |
| ключ из нескольких частей: склад и ячейка | — | пока соединяйте в строку: "A-3" |
Map в официальных материалах (англ.)
- Go maps in action — https://go.dev/blog/maps Старая, но точная статья из блога Go: объявление,
v, ok,delete, обход, и раздел про порядок обхода — тот же вывод, что на странице 4: «когда нужен порядок, сортируйте ключи отдельно».
- Спецификация: Map types — https://go.dev/ref/spec#Map_types Какие типы годятся в ключи и почему именно сравнимые. Про порядок обхода — раздел For statements with range clause: порядок не определён.
- Пакет maps — https://pkg.go.dev/maps
Keys,Values,Equal,Clone,Copy,DeleteFunc. Сегодня нужныKeysиEqual; остальные — в справкеgo doc maps.
- Пакет slices, функция Sorted — https://pkg.go.dev/slices#Sorted Вторая половина связки.
Как map устроена внутри (англ.)
- Faster Go maps with Swiss Tables — https://go.dev/blog/swisstable Как устроена map начиная с Go 1.24: группы по восемь ячеек, поиск по короткому отпечатку ключа. Устройство может меняться от версии к версии — поэтому язык и не обещает никакого порядка обхода.
- Range over function types — https://go.dev/blog/range-functions Что на самом деле возвращает
maps.Keys: итератор. В курсе итераторы будут позже.
Мини-опыты
Попробуйте сейчас:
clearиlen.Цель: увидеть разницу между «очистить map» и «создать новую».
1. В черновике создайте map из трёх пар, передайте её в функцию, которая вызывает встроенную
clear(m), и напечатайтеlenвmainпосле вызова.2. Замените в функции
clear(m)наm = map[string]int{}и снова напечатайтеlenвmain.▶ Выполнитеcd ~/gocourse/day18/scratch go run .Готово, когда: вы можете объяснить по картинке со страницы 5 и по разделу «Новая map внутри функции», почему два запуска напечатали разную длину.
Попробуйте сейчас: map срезов.
Цель: сгруппировать номера строк по артикулу.
1. Значением map может быть срез:
map[string][]int. Прочитайте со ввода артикулы и для каждого запишите номера строк, где он встретился:lines[sku] = append(lines[sku], n). Нулевое значение для среза —nil, иappendк нему работает — так же, как++к нулю у счётчика.2. Напечатайте по возрастанию артикула:
K-417 [1 3].▶ Выполнитеprintf 'K-417\nM-120\nK-417\n' | go run .Готово, когда: у каждого артикула напечатаны все его номера строк в порядке ввода.