Mapの値としてのポインタ
CoddyのGOジャーニー「論理とフロー」セクションの一部。レッスン 27/68。
構造体をマップに直接格納することは便利ですが、構造体へのポインターを格納すると、強力な機能が利用できるようになります。それは、マップを通じて元の構造体データを直接変更できる機能です。この方法は、データを格納した後に更新する必要がある場合に不可欠です。
構造体へのポインターを 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},
}データを変更する必要があるときに、その主な利点が明確になります。ポインターを使っているため、map を通じて行った変更は元の struct に影響します。
// マップを通じてラップトップの価格を直接更新する
products["laptop"].Price = 899.99
fmt.Println(products["laptop"].Price) // 出力: 899.99このパターンは、inventory システム、ユーザープロフィール、または map が主要なデータストアとして機能し、変更を保持する必要があるあらゆる状況など、保存されたデータを頻繁に更新する必要があるアプリケーションで特に有用です。
チャレンジ
簡単map の値として構造体へのポインターを使用する強力さを示す、シンプルな在庫管理システムを作成してください。このチャレンジでは、ポインターを使った map によって保存されたデータを直接変更できるため、在庫システムを効率的にし、更新にすばやく対応できることを示します。
3つの入力を受け取ります:
- 商品の数を表す文字列(例:
"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") - 次の形式で更新操作を含む文字列:
"operation1:product1:value1,operation2:product2:value2"。操作は"price"または"quantity"のいずれかです(例:"price:laptop:899.99,quantity:mouse:25,price:keyboard:65.00")
あなたの課題は次のとおりです:
- 次のフィールドを持つ
Product構造体をDefineする:Price(float64)- 商品の価格Quantity(int)- 商品の在庫数
- キーが商品名(文字列)、値が
Product構造体へのポインターである map をCreateする - 2番目の入力から商品データを解析する:
- 入力を commas で分割して、individual な商品 entries を取得する
- 各 entry について、colons で分割し、名前、価格、数量を分ける
- 価格の文字列を float64 に、数量の文字列を整数に Convert する
Product構造体を Create し、その pointer を map に保存する
- 解析中、商品名を入力に現れる order のまま、別のスライスに保持する。Go で map を range するとキーがランダムな order で処理されるため、すべての一覧を入力 order に保つのはこのスライスです。
"Initial Inventory:"というヘッダーで Initial 在庫を Display する- 各商品を次の Format で表示する:
"[name]: $[price] (Stock: [quantity])" - 3番目の入力から更新操作を Parse して apply する:
- 入力を commas で分割して、individual な operations を取得する
- 各 operation について、colons で分割して、operation の種類、商品名、新しい値を取得する
- map の pointer を通じて appropriate な field を directly 更新する
- 各更新を次の Format で出力する:
"Updated [product]: [field] changed to [new_value]"
"Updated Inventory:"というヘッダーで更新後の在庫を Display する- Initial の表示と同じ Format で各商品をもう一度表示する
- 在庫の合計値を Calculate して表示する:
"Total Inventory Value: $[total_value]"
strconv package を使用して、価格と数量の文字列をそれぞれ対応する数値型に Convert してください。表示時には、すべての価格を小数点以下2桁に Format します。このチャレンジでは、map に pointer を保存すると元の構造体データを directly 変更できるため、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 を取得する
// - マップのポインタを通じて適切なフィールドを直接更新する
// - 各操作の更新メッセージを出力する
// TODO: Display updated inventory
// 再度、一貫した出力のために order スライスを使用する
// 表示しながら合計値を計算する
// TODO: Calculate and display total inventory value
// Format: "Total Inventory Value: $[total_value]"
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
論理とフローのすべてのレッスン
自分で練習してみよう: Goオンラインコンパイラ