ソリューションは、新しい単語が見つかるたびに、入力リストを数回繰り返します。それを行う代わりに、リストを1回だけ繰り返して、すべての単語のすべての出現回数を保持する辞書を作成することができます。
これを機能的なスタイルで行うMap
には、不変の辞書であるF#を使用できます。
let countWords words =
// Increment the number of occurrences of 'word' in the map 'counts'
// If it isn't already in the dictionary, add it with count 1
let increment counts word =
match Map.tryFind word counts with
| Some count -> Map.add word (count + 1) counts
| _ -> Map.add word 1 counts
// Start with an empty map and call 'increment'
// to add all words to the dictionary
words |> List.fold increment Map.empty
同じことを命令型スタイルで実装することもできます。これは、より効率的ですが、エレガントではありません(そして、機能的なスタイルのすべての利点を得るわけではありません)。ただし、標準のミュータブルDictionary
はF#からもうまく使用できます(これはC#バージョンに似ているため、ここでは記述しません)。
最後に、標準のF#関数のみを使用した単純なソリューションが必要な場合はSeq.groupBy
、padで提案されているように使用できます。Dictionary
これはおそらくベースバージョンとほぼ同じくらい効率的です。ただし、F#を学習しているだけの場合countWords
は、自分のような再帰関数をいくつか作成することを学ぶのに最適な方法です。
あなたのコードについていくつかのコメントを与えるために-あなたのアプローチの複雑さは少し高いですが、それはおそらく問題ないはずです。ただし、いくつかの一般的な問題があります。
あなたのcountword2
関数には、がありますif h = wrd then ... else last @ [h], count
。last @ [h]
リスト全体のクローンを作成する必要があるため、この呼び出しは非効率的last
です。これの代わりにh::last
、順序は重要ではないので、単語を先頭に追加するように書くことができます。
最後の行では、で@
再び使用してい[(wrd, count)] @ countword temp
ます。これは必要ありません。リストの先頭に単一の要素を追加する場合は、次を使用する必要があります(wrd,count)::(countword temp)
。