Con trỏ làm giá trị của map
Thuộc phần Logic và luồng điều khiển trong hành trình GO của Coddy. Bài học 27 trên 68.
Dù lưu trực tiếp các struct trong map rất hữu ích, lưu con trỏ đến struct còn mở ra một khả năng mạnh mẽ: chỉnh sửa trực tiếp dữ liệu của struct ban đầu thông qua map. Cách này rất cần thiết khi bạn cần cập nhật dữ liệu sau khi đã lưu.
Khi lưu một con trỏ đến struct làm giá trị trong map, bạn đang lưu địa chỉ bộ nhớ của struct chứ không phải bản sao của nó. Sau đây là cách khai báo và thao tác với map như vậy:
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},
}Ưu điểm chính thể hiện rõ khi bạn cần sửa đổi dữ liệu. Vì bạn đang làm việc với các con trỏ, những thay đổi qua map sẽ ảnh hưởng đến struct gốc:
// Cập nhật giá của laptop trực tiếp qua map
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // in ra: 899.99Mẫu này đặc biệt hữu ích trong các ứng dụng cần thường xuyên cập nhật dữ liệu đã lưu, chẳng hạn như hệ thống quản lý hàng tồn kho, hồ sơ người dùng hoặc bất kỳ tình huống nào mà bản đồ đóng vai trò là kho dữ liệu chính và các thay đổi cần được lưu lại.
Thử thách
DễXây dựng một hệ thống quản lý hàng tồn kho đơn giản để minh họa sức mạnh của việc dùng con trỏ đến struct làm giá trị trong map. Thử thách này cho thấy map dùng con trỏ cho phép chỉnh sửa trực tiếp dữ liệu đã lưu, giúp hệ thống quản lý hàng tồn kho hoạt động hiệu quả và phản hồi nhanh khi có cập nhật.
Bạn sẽ nhận được ba đầu vào:
- Một chuỗi biểu thị số lượng sản phẩm (ví dụ:
"4") - Một chuỗi chứa dữ liệu sản phẩm theo định dạng:
"name1:price1:quantity1,name2:price2:quantity2,name3:price3:quantity3"(ví dụ:"laptop:999.99:5,mouse:25.50:20,keyboard:75.00:15,monitor:299.99:8") - Một chuỗi chứa các thao tác cập nhật theo định dạng:
"operation1:product1:value1,operation2:product2:value2", trong đó thao tác có thể là"price"hoặc"quantity"(ví dụ:"price:laptop:899.99,quantity:mouse:25,price:keyboard:65.00")
Nhiệm vụ của bạn là:
- Định nghĩa một struct
Productvới các trường sau:Price(float64), giá của sản phẩmQuantity(int), số lượng sản phẩm trong kho
- Tạo một map có khóa là tên sản phẩm (chuỗi) và giá trị là con trỏ đến các struct
Product - Phân tích dữ liệu sản phẩm từ đầu vào thứ hai:
- Tách đầu vào theo dấu phẩy để lấy từng mục dữ liệu sản phẩm
- Với mỗi mục, tách theo dấu hai chấm để phân chia tên, giá và số lượng
- Chuyển chuỗi giá thành số thực float64 và chuỗi số lượng thành số nguyên
- Tạo một struct
Productvà lưu con trỏ đến struct đó trong map
- Khi phân tích dữ liệu, giữ tên sản phẩm trong một slice riêng theo đúng thứ tự xuất hiện trong đầu vào. Khi duyệt map trong Go, các khóa sẽ xuất hiện theo thứ tự ngẫu nhiên, vì vậy slice giúp giữ nguyên thứ tự đầu vào cho mọi danh sách sản phẩm.
- Hiển thị hàng tồn kho ban đầu với tiêu đề
"Initial Inventory:" - Hiển thị từng sản phẩm theo định dạng:
"[name]: $[price] (Stock: [quantity])" - Phân tích và áp dụng các thao tác cập nhật từ đầu vào thứ ba:
- Tách đầu vào theo dấu phẩy để lấy từng thao tác
- Với mỗi thao tác, tách theo dấu hai chấm để lấy loại thao tác, tên sản phẩm và giá trị mới
- Cập nhật trực tiếp trường thích hợp thông qua con trỏ trong map
- In từng cập nhật theo định dạng:
"Updated [product]: [field] changed to [new_value]"
- Hiển thị hàng tồn kho đã cập nhật với tiêu đề
"Updated Inventory:" - Hiển thị lại từng sản phẩm theo cùng định dạng như phần hiển thị ban đầu
- Tính và hiển thị tổng giá trị hàng tồn kho:
"Total Inventory Value: $[total_value]"
Dùng package strconv để chuyển chuỗi giá và số lượng thành các kiểu số tương ứng. Định dạng tất cả giá với 2 chữ số thập phân khi hiển thị. Thử thách này minh họa cách lưu con trỏ trong map cho phép bạn chỉnh sửa trực tiếp dữ liệu struct ban đầu, giúp các cập nhật hàng tồn kho có hiệu lực ngay lập tức và được giữ lại mà không cần gán lại giá trị vào map.
Tự làm thử
package main
import (
"fmt"
"strconv"
"strings"
)
func main() {
// Đọc đầu vào
var numProductsStr string
var productDataStr string
var operationsStr string
fmt.Scanln(&numProductsStr)
fmt.Scanln(&productDataStr)
fmt.Scanln(&operationsStr)
// TODO: Định nghĩa struct Product tại đây
// TODO: Tạo map để lưu các con trỏ đến sản phẩm
// TODO: Tạo slice để giữ thứ tự sản phẩm (đảm bảo đầu ra nhất quán)
// TODO: Phân tích dữ liệu sản phẩm và thêm vào map
// Nhớ thực hiện:
// - Tách productDataStr theo dấu phẩy để lấy từng mục
// - Với mỗi mục, tách theo dấu hai chấm để lấy tên, giá và số lượng
// - Chuyển các chuỗi giá và số lượng sang kiểu dữ liệu phù hợp
// - Lưu con trỏ đến struct Product vào map
// - Thêm tên sản phẩm vào slice thứ tự
// TODO: Hiển thị kho hàng ban đầu
// Dùng lát cắt order để duyệt sản phẩm theo thứ tự nhất quán
// Định dạng: "[name]: $[price] (Stock: [quantity])"
// TODO: Phân tích và áp dụng các thao tác cập nhật
// Nhớ thực hiện:
// - Tách operationsStr theo dấu phẩy để lấy từng thao tác
// - Với mỗi thao tác, tách theo dấu hai chấm để lấy loại, tên và giá trị
// - Cập nhật trực tiếp trường tương ứng qua con trỏ trong map
// - In ra thông báo cập nhật cho mỗi thao tác
// TODO: Hiển thị kho hàng sau khi cập nhật
// Tiếp tục dùng lát cắt order để đầu ra có thứ tự nhất quán
// Tính tổng giá trị trong khi hiển thị
// TODO: Tính và hiển thị tổng giá trị kho hàng
// Định dạng: "Total Inventory Value: $[total_value]"
}Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Logic và luồng điều khiển
1Luồng điều khiển nâng cao
Switch với `fallthrough`Thoát khỏi vòng lặp lồng nhauTiếp tục một vòng lặp cụ thểCâu lệnh `goto`Ôn tập: điều khiển vòng lặp nâng cao4Dự án: Danh sách việc cần làm đơn giản
Thiết lập dự ánThêm một việc cần làm2Struct và phương thức
Định nghĩa phương thức cho structReceiver giá trịReceiver con trỏChọn receiverPhương thức và hàmÔn tập: hành vi của struct5Tìm hiểu chuyên sâu về map
Map chứa structCon trỏ làm giá trị của mapKiểm tra map nilSo sánh các mapÔn tập: bộ đếm tần suất từ8Dự án: Quản lý hàng tồn kho đơn giản
Tổng quan dự ánKiểm tra hàng tồn kho3Interface (cơ bản)
Interface là gì?Định nghĩa interfaceTriển khai interfaceDùng kiểu interfaceInterface rỗngKhẳng định kiểuType switchÔn tập: hình dạng và hành viTự luyện tập: Trình biên dịch Go online