Go言語ではset型がないのでmapを使おう
Go言語を業務で使い始めて2ヶ月ほどが経ちました。
業務の中で知らなかったせいで恥をかいてしまったGo言語特有の変わった実装がありました。反省の意味も込めて紹介したいと思います。
(noteの記事でコードブロックを使ったことがなかったので、一回試しに使ってみたいという裏テーマもあります。 )
Go言語には set型 がない!
Go言語には struct や interface、mapなどの型が事前に用意されているものの、実は set型が用意されていません。
ここでいうset型とは、数学で言う集合のことで、重複しない要素が集まったものを表現したい時に用います。Pythonには組込型として提供されていて、以下のように簡単に使うことができます。
myset = set()
myset.add(1)
myset.add(2)
myset.add(2)
print(myset)
# Output: {1, 2}
Go言語でのmapとstruct
Go言語では連想配列型を定義するためのmapがあります。
map[int64]string
が表すのは、int64をキー、stringを値とする連想配列の型です。
また、Go言語にはstruct{}で独自の型を定義できます。
type MyStruct struct {
Name string
Age int64
}
この例だと、MyStruct型 は、stringとint64を属性として持つことができる構造体の型です。
Go言語で set型(みたいなもの)を使いたい
さて、本題のset型は次のように実装するらしいです(stringをキーとする時)。
type MySet map[string]struct{}
なにこれ?
stringをキー、struct{}型を値とする連想配列の型がset型と同じように使えるらしいのですが、どういう意味でしょうか。
この型にキーと値を設定する場合、値は必ず struct{}{} です。
mySet := make(map[string]struct{})
mySet["hoge"] = struct{}{}
mySet["geho"] = struct{}{}
mySet["geho"] = struct{}{}
ここで代入したキーの一覧はまさに欲しかった重複のない要素の集合になるのです。
for val := range mySet {
fmt.Println(val)
}
// Output:
// hoge
// geho
map型が持つ性質である「キーに対してただ一つの値が定まる」ことを活かして、set型としての要素の一意性を実現しているわけです。
つまり、値の型はぶっちゃけなんでも良いことになってしまいますが、struct{}型を用いるメリットは、値のサイズが0バイトになり、最も効率が良いかららしいです。
「キーが存在しているかどうか=set型に要素が含まれているか」になります。
if _, ok := mySet["someKey"]; ok {
fmt.Println("someKey exists in mySet")
} else {
fmt.Println("someKey doesn't exist in mySet")
}
英語ですが、以下の記事が用例がたくさんあり参考になります。
まとめ
map[<type of key>]struct{} がset型として機能することがわかりました。
奇妙な見た目をしていますが、余計な機能を極力省き、必要最小限の機能を組み合わせて応用していくまさにGo wayを感じるお話でした。
(裏テーマ感想)
noteでコードブロックを使ってみたけど、言語を選択できないのでシンタックスハイライトが神頼みならぬnote頼みであるのが困り物。
よろしければサポートお願いします!本の購入などに充てます!