Menu
Coddy logo textTech

Указатели как значения карт

Часть раздела Логика и управление потоком путешествия по 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, а изменения должны сохраняться.

challenge icon

Задание

Легко

Создай простую систему управления 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")

Твоя задача:

  1. Define структуру Product со следующими полями:
    • Price (float64) — цена продукта
    • Quantity (int) — количество продукта на складе
  2. Create map, где ключами являются названия продуктов (строки), а значениями — указатели на структуры Product
  3. Разобрать данные о продуктах из второго input:
    • Разделить input по commas, чтобы получить individual записи о продуктах
    • Для each entry разделить данные по colons, чтобы отделить название, цену и количество
    • Convert строку с ценой в float64, а строку с количеством — в целое число
    • Create структуру Product и сохранить указатель на неё в map
  4. Сохранять названия продуктов в отдельном срезе во время разбора, в order их появления в input. Перебор map в Go посещает ключи в случайном порядке, поэтому именно срез сохраняет каждую запись в порядке input.
  5. Display исходный Inventory с заголовком "Initial Inventory:"
  6. Показывать каждый продукт в формате: "[name]: $[price] (Stock: [quantity])"
  7. Разобрать и apply обновления operations из третьего input:
    • Разделить input по commas, чтобы получить individual operations
    • Для each operation разделить данные по colons, чтобы получить тип operation, название продукта и новое значение
    • Обновить appropriate поле directly через pointer в map
    • Вывести каждое обновление в формате: "Updated [product]: [field] changed to [new_value]"
  8. Display обновлённый Inventory с заголовком "Updated Inventory:"
  9. Снова показать каждый продукт в том же формате, что и при Initial отображении
  10. 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]"
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Логика и управление потоком

Потренируйтесь самостоятельно: Онлайн-компилятор Go