Go/マップ
< Go
Map型[編集]
Goでは連想配列(ハッシュテーブル)のことをmapと呼びます。
マップの生成と様々な操作[編集]
- マップの生成と様々な操作
package main import "fmt" func main() { m := map[string]int{ "アップルジュース": 150, "牛乳": 100, "サイダー": 180, } printEntry(m, "牛乳") printEntry(m, "レモンジュース") m["レモンジュース"] = 110 printEntry(m, "レモンジュース") delete(m, "牛乳") printEntry(m, "牛乳") delete(m, "コーヒー") fmt.Println("m =", m) fmt.Printf("%%v = %v\n", m) fmt.Printf("%%+v = %+v\n", m) fmt.Printf("%%#v = %#v\n", m) fmt.Println("len(m) =", len(m)) for k, v := range m { fmt.Printf("for::%v は %v 円です。\n", k, v) } } func printEntry(m map[string]int, key string) { if v, ok := m[key]; ok { fmt.Printf("%v は %v 円です。\n", key, v) } else { fmt.Printf("%v の取り扱いは有りません。\n", key) } }
- 実行結果
牛乳 は 100 円です。 レモンジュース の取り扱いは有りません。 レモンジュース は 110 円です。 牛乳 の取り扱いは有りません。 m = map[アップルジュース:150 サイダー:180 レモンジュース:110] %v = map[アップルジュース:150 サイダー:180 レモンジュース:110] %+v = map[アップルジュース:150 サイダー:180 レモンジュース:110] %#v = map[string]int{"アップルジュース":150, "サイダー":180, "レモンジュース":110} len(m) = 3 for::アップルジュース は 150 円です。 for::サイダー は 180 円です。 for::レモンジュース は 110 円です。
- 解説
m := map[string]int{ "アップルジュース": 150, "牛乳": 100, "サイダー": 180, }
- stringをキーにintを値に持つMap型の変数 m を宣言し、3エントリーのリストで初期化しています。
- 値の参照
func printEntry(m map[string]int, key string) { if v, ok := m[key]; ok { fmt.Printf("%v は %v 円です。\n", key, v) } else { fmt.Printf("%v の取り扱いは有りません。\n", key) } }
- マップからキーに対する値を参照するときのイディオムです。
- 頻回に使うので関数にしました。
- Goでは関数の前方参照を関数宣言なしにできます(そもそも関数定義を伴わない関数宣言はGoにはありません)。
- 単に
v := m[key]
とするとkeyに対応するエントリがなかった場合、vには値の初期値が返ります(この場合はintの初期値の 0 で nil ではありません) - このため2つ目の戻り値(この場合は ok)を評価してエントリーの有無を調べる必要があります。
- エントリーの追加
printEntry(m, "レモンジュース") m["レモンジュース"] = 110 printEntry(m, "レモンジュース")
- 実行結果
レモンジュース の取り扱いは有りません。 レモンジュース は 110 円です。
- 元はエントリーがなかった状態から、新たなキーと値を与えると、エントリーを追加できます。
- エントリーの削除
delete(m, "牛乳") printEntry(m, "牛乳")
- 実行結果
牛乳 の取り扱いは有りません。
- エントリーの削除は組込み関数 delete を使います。
- 存在しないキーを使った削除
delete(m, "コーヒー")
- 存在しないキーを使って削除しても何も起こりません(panic も投げられません)。
- マップなるごと文字列化
fmt.Println("m =", m) fmt.Printf("%%v = %v\n", m) fmt.Printf("%%+v = %+v\n", m) fmt.Printf("%%#v = %#v\n", m)
- 実行結果
m = map[アップルジュース:150 サイダー:180 レモンジュース:110] %v = map[アップルジュース:150 サイダー:180 レモンジュース:110] %+v = map[アップルジュース:150 サイダー:180 レモンジュース:110] %#v = map[string]int{"アップルジュース":150, "サイダー":180, "レモンジュース":110}
- fmt.Printf の %v は fmt.Println と同じフォーマットです。
- %+v も同じです(%+vは、構造体の場合フィールド名も表示します)
- %#v は、複合リテラルのフォーマットです
- エントリー数を得る
fmt.Println("len(m) =", len(m))
- エントリー数は組込み関数 len を使って得ます。
- イテレーション
for k, v := range m { fmt.Printf("for::%v は %v 円です。\n", k, v) }
- マップもコレクションの一種なので for 文の range 句の対象にできます。
- サンプルコードの通りキーと値をペアごとに繰り返します。
様々な操作をメソッド化[編集]
単価表を1つの型として定義して、様々な操作をメソッドにしてみました。 メソッドチェーンができるよう、自分自身を戻値にしました。
- 様々な操作をメソッド化
package main import "fmt" type PriceTable map[string]int func main() { PriceTable{ "アップルジュース": 150, "牛乳": 100, "サイダー": 180, }. print("牛乳"). print("レモンジュース"). set("レモンジュース", 110). print("レモンジュース"). remove("牛乳"). print("牛乳"). remove("コーヒー"). each(func(k string, v int) { fmt.Printf(" each::%v は %v 円です。\n", k, v) }) } func (pt PriceTable) get(key string) int { return pt[key] } func (pt PriceTable) set(key string, v int) PriceTable { pt[key] = v return pt } func (pt PriceTable) print(key string) PriceTable { if v, ok := pt[key]; ok { fmt.Printf("%v は %v 円です。\n", key, v) } else { fmt.Printf("%v の取り扱いは有りません。\n", key) } return pt } func (pt PriceTable) remove(key string) PriceTable { delete(pt, key) return pt } func (pt PriceTable) len() int { return len(pt) } func (pt PriceTable) each(f func(k string, v int)) PriceTable { for k, v := range pt { f(k, v) } return pt }
- 実行結果
牛乳 は 100 円です。 レモンジュース の取り扱いは有りません。 レモンジュース は 110 円です。 牛乳 の取り扱いは有りません。 each::アップルジュース は 150 円です。 each::サイダー は 180 円です。 each::レモンジュース は 110 円です。
- 解説
- 型定義
type PriceTable map[string]int
- メソッドは(組込み型ではなく)定義された型にしか定義できないので
map[string]int
の別名PriceTable
を用意します。 - 複合リテラルとしてインスタンス化
PriceTable{ "アップルジュース": 150, "牛乳": 100, "サイダー": 180, }.
- 最後の行の行末の .(ピリオド)がポイントでメソッド(あるいはフィールド)が続く事を表します(ここに . がないと ; が自動的に挿入されてしまう)。
- メソッドチェイン
print("牛乳"). print("レモンジュース"). set("レモンジュース", 110). print("レモンジュース"). remove("牛乳"). print("牛乳"). remove("コーヒー"). each(func(k string, v int) { fmt.Printf(" each::%v は %v 円です。\n", k, v) })
- 様々な操作を(チェイン可能な)メソッドにしました。
- each は繰り返し処理を抽象化しました。