Go Exercise:Map

goの基本的な文法を学ぶA Tour of Go。所々で理解度を確認する為にエクササイズが用意されています。これが意外と曲者と感じているこの頃です。

何が曲者かというと(英語でやっているからかもしれませんが)何をすれば良いか直接書いてありません。例えば今回のmapという機能のエクササイズの文章がいかになります。

Exercise: Maps
Implement WordCount. It should return a map of the counts of each “word” in the string s. The wc.Test function runs a test suite against the provided function and prints success or failure.
You might find strings.Fields helpful.

で英語に自信がない私は大体わからなかったらdeepLを使います。翻訳結果が以下

演習。マップ
WordCountを実装します。wc.Test関数は、提供された関数に対してテストスイートを実行し、成功か失敗かを表示します。
strings.Fieldsが役に立つかもしれません。

どうやら理解出来なかったのは私の英語力だけが原因ではなく本文そのものも一般向けではない表現をしているみたいです。
とにかくわからない時は、実行してみるに限ります。

package main
import (
	"golang.org/x/tour/wc"
)
func WordCount(s string) map[string]int {
	return map[string]int{"x": 1}
}
func main() {
	wc.Test(WordCount)
}

// 実行結果
FAIL
f("I am learning Go!") =
 map[string]int{"x":1}
want:
 map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
 

うーん?いまいちわからないので次のようにして実行します。

package main
import (
	"golang.org/x/tour/wc"
)
func WordCount(s string) map[string]int {
	return map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
}
func main() {
	wc.Test(WordCount)
}

PASS
f("I am learning Go!") = 
 map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
FAIL
f("The quick brown fox jumped over the lazy dog.") =
 map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
want:
 map[string]int{"The":1, "brown":1, "dog.":1, "fox":1, "jumped":1, "lazy":1, "over":1, "quick":1, "the":1}

なるほど、wc.Test()が適当な文字列とその答えを用意して、文字列をこちらが作ったwordCount関数に代入し実行、帰ってきた値と答えが一致するかを判定しているみたいです。
どうやら単語とそれがいくつ含まれているかをmapにして返して欲しいみたいです。やっとやるべきことがわかりました。

ということでやることは大まかに次の二つです。
・文章を単語ごとに分けて配列にする。
 →split関数として実装
・配列の単語がいくつ含まれているかをカウントしてmapにする。
 →WordCount関数として実装

split関数

func split(article string) []string {
	var words []string
	var s string
	for _, w := range article {
		if string(w) == " " {
			words = append(words, s)
			s = ""
		} else {
			s += string(w)
		}
	}
	words = append(words, s)
	return words
}

英語の場合は単語の間に空白があるので空白を条件にします。文字列はそれ自体が配列なのでrangeでループを回すことで1文字ずつ取り出して、単語を保持する仮の変数sにアペンドします。

取り出した文字が空白の場合は、単語と単語の切れ目なのでsには一つの単語が入っていることになります。なので単語の入っているsを配列wordsにアペンドし、sを初期化します。

これだと最後の単語のあとは空白がないので、最後の単語に関してはループ文の後にwordsにアペンドしてあげます。wordsをリターンしてあげれば完成です。

WordCount関数

func WordCount(article string) map[string]int {
	word_list := split(article)
	words := make(map[string]int)
	for _, w := range word_list {
		words[w]++
	}
	return words
}

ここではsplit関数で単語の配列にしたものを用いて含まれる単語の関数をカウントしmapで返します。

まず文章を引数で受け取り、split関数で単語の配列にします。それをword_listという変数に入れます。また、最後に返すmap変数wordsを作ります。

word_listをループ文にで回し、配列の要素一つ一つをw代入します。このwをプロパティとしてwords[w]をインクリメントします。wが初めて登場する単語ならwordsの中にwのプロパティを作り値を0から1にインクリメントします。2回目以降は、1が2、2が3と増えていくだけです。
最後にこのwordsを返してあげれば完了です。実行結果が以下になります。

PASS
f("I am learning Go!") = 
 map[string]int{"Go!":1, "I":1, "am":1, "learning":1}
PASS
f("The quick brown fox jumped over the lazy dog.") = 
 map[string]int{"The":1, "brown":1, "dog.":1, "fox":1, "jumped":1, "lazy":1, "over":1, "quick":1, "the":1}
PASS
f("I ate a donut. Then I ate another donut.") = 
 map[string]int{"I":2, "Then":1, "a":1, "another":1, "ate":2, "donut.":2}
PASS
f("A man a plan a canal panama.") = 
 map[string]int{"A":1, "a":2, "canal":1, "man":1, "panama.":1, "plan":1}

最近はAPIとかばっかり叩いていてこういう基本的な操作はしていなかったので良い頭の体操になりました。

興味があれば上のplaygroundで他の文章でも試して見てください!

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