単語の出現数を数える

C++

//
// word_count.cpp
//

#include 
#include 
#include 
#include 

using namespace std;

// 単語の出現数を数える
// 単語の List から,単語→出現回数の Map を生成して返す
map& word_count(list& strs)
{
    map* word_map_ptr = new map();
    map& word_map = *word_map_ptr;

    // List の単語を順番に見ていく
    for ( list::iterator it = strs.begin();
          it != strs.end(); ++it )
    {
        // Map のキーとして単語が存在するか?
        if ( word_map.find(*it) != word_map.end() ) {
            // 単語が存在するとき,出現回数を増加
            word_map[*it]++;
        } else {
            // 単語が存在しないとき,出現回数を 1 に初期化
            word_map[*it] = 1;
        }
    }

    return word_map;
}

int main()
{
    list       strs;
    map   word_map;

    // List に単語を追加
    strs.push_back("Alice");
    strs.push_back("Bob");
    strs.push_back("Alice");

    // 単語の出現数を数える
    // 単語の List から,単語→出現回数の Map を生成して返す
    word_map = word_count(strs);

    // Map の単語と出現回数を順番に見ていく
    for ( map::iterator it = word_map.begin();
          it != word_map.end(); ++it)
    {
        // first が単語,second が出現回数
        // first が Map のキー,second が Map の値
        // first, second はメソッドではなくメンバ変数
        cout << it->first << " : " << it->second << endl;
    }

    return 0;
}
$ g++ word_count.cpp
$ ./a.out
Alice : 2
Bob : 1
$

C++ で書いてみたけれど,C++ の流儀が分からないのでこれでいいのかよく分からない.メモリリークもしているかもしれない.

気持ち悪い点は,

  • map::find() がイテレータを返すこと.特に,キーが見つからないとき iterator::end() を返すこと.
  • map は pair のコンテナである,という考えのため,map のキーが first,map の値が second という変数名になっていること.

Ruby

#
# word_count.rb
#

# Array に単語を追加
ary = ['Alice', 'Bob', 'Alice']

# デフォルト値が 0 (Integer) の Hash を生成
h = Hash.new(0)

# Array の単語を順番に見ていく
ary.each do |str|
  # 単語の出現回数を増加
  h[str] += 1
end

# Hash の中身を表示
p h

# Hash の単語と出現回数を順番に見ていく
h.each do |key, value|
  # key が単語,value が出現回数
  print key, " : ", value, "\n"
end
$ ruby word_count.rb
{"Alice"=>2, "Bob"=>1}
Alice : 2
Bob : 1
$

Ruby で書いてみた.圧倒的に簡潔になる.

Haskell

{-
 word_count.hs
 -}

-- List ライブラリのインポート
import List

-- 単語のリスト
word_list :: [String]
word_list = ["Alice", "Bob", "Alice"]

{-
  sort … リストをソートする
      ["Alice", "Bob", "Alice"] -> ["Alice", "Alice", "Bob"]
  group … リスト中で隣接する同じ要素をリストにまとめる
      ["Alice", "Alice", "Bob"] -> [["Alice", "Alice"], ["Bob"]]
  head … リストの最初の要素を取得
      ["Alice", "Alice"] -> "Alice"
      ["Bob"] -> "Bob"
  length … リストの長さを取得
      ["Alice", "Alice"] -> 2
      ["Bob"] -> 1
  \xs -> 〜
  … xs を引数とする無名関数
  \xs -> (head xs, length xs)
  … 引数 xs に head, length を適用した組 (head xs, length xs) を返す
      ["Alice", "Alice"] -> ("Alice", 2)
      ["Bob"] -> ("Bob", 1)
  map f xs … リスト xs の各要素に関数 f を適用したもののリストを返す
      [x1, x2, ..., xn] -> [(f x1), (f x2), ..., (f xn)]
      [["Alice", "Alice"], ["Bob"]] -> [("Alice", 2), ("Bob", 1)]
 -}
word_count :: [String] -> [(String, Int)]
word_count = (map (\xs -> (head xs, length xs))) . group . sort

main = print (word_count word_list)
$ ghc --make word_count.hs
Chasing modules from: word_count.hs
Compiling Main             ( word_count.hs, word_count.o )
Linking ...
$ ./a.out
[("Alice", 2), ("Bob", 1)]
$

ムシャクシャしてやった.言語なら何でもよかった.今は反省している
Haskell は簡潔に書けるが,知らない人には全く読めないだろうし,知っている人はもっとうまく書くかもしれない.