Go 言語で map(Key-Value 型の仕組み)をサンプルコードを通して理解する
Contents
はじめに
今回のエントリではここまで作成したモジュールを変更して複数の人々に挨拶を返却したいと思います。
言い換えると「複数の入力」を受け取り、その入力とペアになる「複数の出力」を返す仕組みを実現してみます。
当エントリは以下のエントリの続きとなっています。事前に一読しておくことをおすすめします。
- Go 言語でパッケージを自作して、それを呼び出してみる – その 1 | Go 言語用ポストイット
- Go 言語でパッケージを自作して、それを呼び出してみる – その 2 | Go 言語用ポストイット
- Go 言語でエラーを返却し、受け取った側で適切に処理する | Go 言語用ポストイット
- Go 言語でランダムな文字列を返却する(
math/rand
ライブラリ ) | Go 言語用ポストイット
また、 スライス についても理解してきましょう。
Hello
関数はパラメータを 1 つしか受け取れないため修正したいが...
ここまで作成した Hello
関数のパラメータを 1 つだけの状態から複数受け取れるように変更したいとしましょう。
しかし、すでに genzouw/greetings
モジュールが公開されており、 Hello
関数の呼び出し元が存在しているため、関数の入力パラメータの型が変更されると壊れてしまいます。
こんなときは、名前の異なる別関数を新規作成します。
他言語でも同様の考えではありますが、Go 言語は特に後方互換を考慮した考えを強く意識しているように感じます。
コーディング開始!
1. greetings/greetings.go
を開き、コードを変更
package greetings
import (
"errors"
"fmt"
"math/rand"
"time"
)
func Hello(name string) (string, error) {
if name == "" {
return "", errors.New("empty name")
}
message := fmt.Sprintf(randomFormat(), name)
return message, nil
}
func Hellos(names []string) (map[string]string, error) {
messages := make(map[string]string)
for _, name := range names {
message, err := Hello(name)
if err != nil {
return nil, err
}
messages[name] = message
}
return messages, nil
}
func init() {
rand.Seed(time.Now().UnixNano())
}
func randomFormat() string {
formats := []string{
"Hi, %v. Welcome!",
"Great to see you, %v!",
"Hail, %v! Well met!",
}
return formats[rand.Intn(len(formats))]
}
前回からの差分は以下になります。
$ git diff
diff --git greetings.go greetings.go
index f4fc944..042e0f4 100644
--- greetings.go
+++ greetings.go
@@ -17,6 +17,18 @@ func Hello(name string) (string, error) {
return message, nil
}
+func Hellos(names []string) (map[string]string, error) {
+ messages := make(map[string]string)
+ for _, name := range names {
+ message, err := Hello(name)
+ if err != nil {
+ return nil, err
+ }
+ messages[name] = message
+ }
+ return messages, nil
+}
+
func init() {
rand.Seed(time.Now().UnixNano())
}
コード解説
かんたんに解説します。
Hellos
関数を追加。Hello
関数では引数として「1 人の名前」を string 型で受け取る形式だったものを「複数の名前」を受け取る形式に変更- 戻り値はもともと string 型の値だったものを map 型に変更
- 追加した
Hellos
関数はHello
関数を呼び出す。- 重複したロジックを減らす工夫となっています。
messages
という map 型変数を作成しています。- この map 型変数を使い、受け取った「名前」 ( キーとして利用 ) とその名前に対する「生成された挨拶文」 ( バリューとして使用 ) をマッピングします。
- Go 言語では、次の構文で map の初期化を行えます。
make(map[key-type]value-type)
- 受け取った
names
パラメータでループし、Hello
関数を呼び出します。range
キーワードでループさせた結果、2 つの値を取得できます。 1 つ目はスライスや配列のインデックス、2 つ目はスライスや配列の値です。- 今回はインデックスは不要なので、Go の ブランク識別子 ( アンダースコアのこと ) を使っています。
2. hello/hello.go
を開き、 コードを変更
package main
import (
"fmt"
"log"
"genzouw/greetings"
)
func main() {
log.SetPrefix("greetings: ")
log.SetFlags(0)
names := []string{"genzouw", "tsubasa", "misaki"}
messages, err := greetings.Hellos(names)
if err != nil {
log.Fatal(err)
}
fmt.Println(messages)
}
前回からの差分は以下になります。
$ git diff
diff --git hello.go hello.go
index 80f2768..b986ec1 100644
--- hello.go
+++ hello.go
@@ -11,10 +11,12 @@ func main() {
log.SetPrefix("greetings: ")
log.SetFlags(0)
- message, err := greetings.Hello("genzouw")
+ names := []string{"genzouw", "tsubasa", "misaki"}
+
+ messages, err := greetings.Hellos(names)
if err != nil {
log.Fatal(err)
}
- fmt.Println(message)
+ fmt.Println(messages)
}
names
変数に「複数の名前」が格納されたスライスを代入します。names
変数をHellos
関数に渡します。Hellos
関数の実行結果をmessages
変数に代入します。
アプリケーション実行
hello
モジュールの配置されているディレクトリで、コードを実行してみます。
実行した結果出力される情報は、 名前とその名前に対するメッセージのペアが格納された map
の文字列表現となっています。
以下のようなメッセージが表示されるはずです。
$ go run .
map[genzouw:Hail, genzouw! Well met! misaki:Hi, misaki. Welcome! tsubasa:Great to see you, tsubasa!]
ひとこと
今回のエントリでは、 「キー」と「値」のペアを取り扱う際に便利な map について取り上げました。
また、新機能を実装する際には新しい関数を実装することで下位互換性を崩さないようにできるということについても取り上げました。
次回は 「Go 言語組み込みのユニットテスト機能」 を触ってみます。
ディスカッション
コメント一覧
まだ、コメントがありません