見出し画像

Golang_mapとカンマOKイディオム#465

他の多くのプログラミング言語と同様、Golangにおいてもmapが存在します。かつ、Python等にはないカンマOKイディオムという便利な書き方もできるので、そのあたりを整理したいと思います。

Mapとは

mapは、キーと値のペアを格納するデータ構造です。キーで値にアクセスするのが非常に効率的で、多くのプログラミング言語で使用されています。Golangのmapは、ハッシュテーブルとして実装されています。

Mapの宣言と初期化

Goでは以下のように宣言できます。

var myMap map[string]int

mapのゼロ値はnilなので、この宣言だけではmyMapはnilの状態です。
make関数を使って初期化できます。

myMap = make(map[string]int)

あるいは、make関数で宣言と初期化を同時に行えます。

myMap := make(map[string]int)

Mapの操作

mapは以下のように操作できます。

myMap["Alice"] = 25 // 追加または更新
age := myMap["Alice"] // 取得
delete(myMap, "Alice") // 削除

myMap.Alice」のようなドットアクセスはできません
Golangにはmapと似たデータ構造で「構造体 (struct)」があり、こちらではドットアクセスを使用できます。

import (
	"fmt"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	p := Person{Name: "Alice", Age: 25}
	fmt.Println(p.Name)  // 出力: Alice
	fmt.Println(p.Age)   // 出力: 25
}

カンマOKイディオム

これはmapのキーの存在チェックや型アサーションなどで頻繁に使用されます。このイディオムを使用することで、値の存在確認と取得を同時に実施できます。

mapでのカンマOKイディオム

mapでは以下のように使用でき、キーが存在すればその値と、ok部分にtrueが入ります。

age, ok := myMap["Alice"]
if ok {
    fmt.Println("Alice's age is", age)
} else {
    fmt.Println("Alice is not in the map")
}

キーが存在しない場合、上記のageには型のゼロ値(ここでは0)が入り、okにはfalseが入ります。

型アサーションでのカンマOKイディオム

少し脇道に反れますが、カンマOKイディオムで型アサーションも可能です。

var i any = "hello"
s, ok := i.(string)
if ok {
    fmt.Println(s)
} else {
    fmt.Println("i is not a string")
}

これはAPI等の外部からJSON形式で値を受け取る際などに有効です。

Mapを使った関数の例

A Tour of Goの演習問題をやってみました。
「"I am YM2021"」などの文章が与えられて、そこで出現する単語とその数を数える関数を実装します。

まずは以下のように実装しました。マップのキーに単語を、値に数を足し上げていくようにしています。文章はstringsパッケージのFields関数を使ってスライスに変換します。

package main

import (
	"golang.org/x/tour/wc"
	"strings"
)



func WordCount(s string) map[string]int {
	counts := make(map[string]int)
	words := strings.Fields(s)
	
	for _, word := range words {
		if _, ok := counts[word]; ok {
			counts[word] += 1
		} else {
			counts[word] = 1
		}
	} 
	
	return counts
}

func main() {
	wc.Test(WordCount)
}

wc.Test()はTour of Goで用意されている関数で、ここにメソッドを渡せば自動で文章を渡して結果の検証をやってくれます。

上記でも目的は果たせましたが、よりブラッシュアップしたのが以下です。

package main

import (
	"golang.org/x/tour/wc"
	"strings"
)

func SimpleWordCount(s string) map[string]int {
	counts := make(map[string]int)
	words := strings.Fields(s)
	
	for _, word := range words {
		counts[word]++
	} 
	
	return counts
}

func main() {
	wc.Test(SimpleWordCount)
}

マップの値がintの場合、単純に++を使うことで足し上げることができます。intのゼロ値が0なので、キー(単語)が初登場の場合は足し上げて1になります。

ここまでお読みいただきありがとうございました!!

参考


この記事が気に入ったらサポートをしてみませんか?