Указатели как значения карт
Часть раздела Логика и управление потоком путешествия по GO на Coddy. Урок 27 из 68.
Хотя хранение структур непосредственно в map полезно, хранение указателей на структуры открывает мощную возможность: способность изменять данные исходной структуры напрямую через map. Этот подход необходим, когда вам нужно обновить данные после их сохранения.
Когда вы храните указатель на структуру в качестве значения map, вы храните адрес структуры в памяти, а не её копию. Вот как объявить и использовать такую map:
type Product struct {
Price float64
Quantity int
}
products := map[string]*Product{
"laptop": &Product{Price: 999.99, Quantity: 5},
"mouse": &Product{Price: 25.50, Quantity: 20},
}Ключевое преимущество становится очевидным, когда вам нужно изменить data. Поскольку вы работаете с указателями, изменения, внесённые через map, влияют на исходную структуру:
// Обновить цену ноутбука напрямую через map
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // выводит: 899.99Этот шаблон особенно полезен в приложениях, где необходимо часто обновлять сохранённые data, например в системах inventory, профилях пользователей или любых сценариях, в которых map служит основным хранилищем data, а изменения должны сохраняться.
Задание
ЛегкоСоздай простую систему управления Inventory, демонстрирующую возможности использования указателей на структуры в качестве значений map. Эта задача показывает, как map на основе указателей позволяют напрямую изменять сохранённые данные, делая твою систему Inventory эффективной и быстро реагирующей на обновления.
Ты получишь три input:
- Строку, представляющую количество продуктов (например,
"4") - Строку, содержащую данные о продуктах в формате:
"name1:price1:quantity1,name2:price2:quantity2,name3:price3:quantity3"(например,"laptop:999.99:5,mouse:25.50:20,keyboard:75.00:15,monitor:299.99:8") - Строку, содержащую operations в формате:
"operation1:product1:value1,operation2:product2:value2", где operations — это либо"price", либо"quantity"(например,"price:laptop:899.99,quantity:mouse:25,price:keyboard:65.00")
Твоя задача:
- Define структуру
Productсо следующими полями:Price(float64) — цена продуктаQuantity(int) — количество продукта на складе
- Create map, где ключами являются названия продуктов (строки), а значениями — указатели на структуры
Product - Разобрать данные о продуктах из второго input:
- Разделить input по commas, чтобы получить individual записи о продуктах
- Для each entry разделить данные по colons, чтобы отделить название, цену и количество
- Convert строку с ценой в float64, а строку с количеством — в целое число
- Create структуру
Productи сохранить указатель на неё в map
- Сохранять названия продуктов в отдельном срезе во время разбора, в order их появления в input. Перебор map в Go посещает ключи в случайном порядке, поэтому именно срез сохраняет каждую запись в порядке input.
- Display исходный Inventory с заголовком
"Initial Inventory:" - Показывать каждый продукт в формате:
"[name]: $[price] (Stock: [quantity])" - Разобрать и apply обновления operations из третьего input:
- Разделить input по commas, чтобы получить individual operations
- Для each operation разделить данные по colons, чтобы получить тип operation, название продукта и новое значение
- Обновить appropriate поле directly через pointer в map
- Вывести каждое обновление в формате:
"Updated [product]: [field] changed to [new_value]"
- Display обновлённый Inventory с заголовком
"Updated Inventory:" - Снова показать каждый продукт в том же формате, что и при Initial отображении
- Calculate и Display общее значение Inventory:
"Total Inventory Value: $[total_value]"
Используй пакет strconv, чтобы Convert строки с ценой и количеством в соответствующие числовые типы. Format все цены с точностью до 2 знаков после запятой при отображении. Эта задача демонстрирует, как хранение указателей в map позволяет напрямую изменять данные исходной структуры, делая обновления Inventory немедленными и сохраняющимися без необходимости заново присваивать значения map.
Попробуйте сами
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
// Прочитать ввод
var numProductsStr string
var productDataStr string
var operationsStr string
fmt.Scanln(&numProductsStr)
fmt.Scanln(&productDataStr)
fmt.Scanln(&operationsStr)
// TODO: Определить структуру Product здесь
// TODO: Создать map для хранения указателей на продукты
// TODO: Создать slice для поддержания порядка продуктов (чтобы обеспечить согласованный вывод)
// TODO: Разобрать данные о продуктах и заполнить map
// Не забудьте:
// - Разделить productDataStr по запятым, чтобы получить отдельные записи
// - Для каждой записи разделить по двоеточиям, чтобы получить name, price, quantity
// - Преобразовать строки price и quantity в соответствующие типы
// - Сохранить указатель на структуру Product в map
// - Добавить имя продукта в order slice
// TODO: Display initial inventory
// Использовать order slice для последовательного перебора продуктов
// Формат: "[name]: $[price] (Stock: [quantity])"
// TODO: Разобрать и применить операции обновления
// Не забудьте:
// - Разделить operationsStr по запятым, чтобы получить отдельные операции
// - Для каждой операции разделить по двоеточиям, чтобы получить type, name, value
// - Обновить соответствующее поле напрямую через указатель map
// - Вывести сообщение об обновлении для каждой операции
// TODO: Display updated inventory
// Снова используйте срез order для согласованного вывода
// Вычисляйте общую стоимость во время отображения
// TODO: Calculate and display total inventory value
// Format: "Total Inventory Value: $[total_value]"
}В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Логика и управление потоком
1Продвинутое управление потоком выполнения
Switch с использованием `fallthrough`Выход из вложенных цикловПродолжение конкретного циклаОператор `goto`Итоги: Продвинутое управление циклами4Проект: Простой список задач
Настройка проектаДобавление задачи2Структуры и методы
Определение методов для структурПолучатели по значениюПолучатели по указателюВыбор типа получателяМетоды против функцийИтоги: поведение структур5Карты: подробный разбор
Карты структурУказатели как значения картПроверка карт на nilСравнение картИтоги: счетчик частоты слов3Интерфейсы (Основы)
Что такое интерфейс?Определение интерфейсаРеализация интерфейсаИспользование интерфейсных типовПустой интерфейсУтверждение типаПереключатель типовИтоги: Фигуры и поведениеПотренируйтесь самостоятельно: Онлайн-компилятор Go