mapを使って形態素にidをふる
C++の標準ライブラリであるmapを使ってシンボルにidを振っていく方法をメモ.自然言語処理をしているため,今回はシンボル=形態素ということにして書いていく.
以下のようなテストファイルに対して,重複なくidを振りたい.
//test.txt
取材
の
帰り
に
、
以前
に
も
買った
こと
の
ある
豆腐
屋
さん
を
覗いたら
美味し
そうな
稲荷
寿司
が
売って
いた
。
C++では,mapという標準ライブラリを使うことで,以前使っていたPythonの辞書型のような処理を書くことが可能らしい.
mapの宣言にはテンプレートを用いて,
std::map<std::string, int> word_dictionary; // 単語とid std::map<std::string, int>::iterator word_iterator; // word_dictionary を扱うイテレータ
idをふる部分では,find()メソッドによってmap内に既に対象の単語が存在するかどうかを確かめる.そして,存在しない場合にのみ,新たな要素として単語とidのペアを追加することで,重複なくidをふることが可能である.
while(ifs && getline(ifs, word)){ if(word_dictionary.find(word) == word_dictionary.end()){ word_dictionary.insert(std::pair<std::string, int>(word, number)); number++; } }
結果の表示は,先ほど作ったイテレータによる逐次アクセスで実現される.
for (word_iterator = word_dictionary.begin(); word_iterator != word_dictionary.end(); word_iterator++){ std::cout << word_iterator->first << " : " << word_iterator->second << std::endl; }
以上をまとめると,次のようなプログラムが書ける.
/* unigram_id.cc 単語にidを振っていく */ #include <iostream> #include <fstream> #include <cstring> #include <map> int main() { std::ifstream ifs("./test.txt"); std::string word; // 処理対象のword int id = 0; // 何種類目の単語か std::map<std::string, int> word_dictionary; // 単語とid std::map<std::string, int>::iterator word_iterator; // word_dictionary を扱うイテレータ // 一行ずつ単語を読み込む // 新しい単語の場合はidとともにmapに挿入 while(ifs && getline(ifs, word)){ if(word_dictionary.find(word) == word_dictionary.end()){ word_dictionary.insert(std::pair<std::string, int>(word, id)); id++; } } //結果の表示 for (word_iterator = word_dictionary.begin(); word_iterator != word_dictionary.end(); word_iterator++){ std::cout << word_iterator->first << " : " << word_iterator->second << std::endl; } return 0; }
実行結果.
確かに,重複なくidをふることができた.
、 : 4
。 : 22
ある : 9
いた : 21
が : 19
こと : 8
さん : 12
そうな : 16
に : 3
の : 1
も : 6
を : 13
以前 : 5
取材 : 0
売って : 20
寿司 : 18
屋 : 11
帰り : 2
稲荷 : 17
美味し : 15
覗いたら : 14
豆腐 : 10
買った : 7
どうやら,イテレータによる逐次アクセスはkeyの昇順にアクセスされるらしい.明示的にソートしなければならないPythonの辞書型と少し異なる部分なのかな.
mapの使い方とかは以下を参考にした.
【C++】mapを使うサンプル。【STL】
http://www.cppll.jp/cppreference/cppmap_details.html
おまけ
mapを使ってペアを保存する際に,その最大保存数が気になったので調べてみた.ひとまず,以下のように様々なサイズの要素を持つmapを用意し,その最大保存数をmax_size()で出力してみる.
/* maxsize_of_map.cc mapの最大保存件数を表示 */ #include <iostream> #include <map> struct Fivebytes{ char c1; int i1; }; struct Longs2{ long l1; long l2; }; struct Longs3{ long l1; long l2; long l3; }; int main() { std::map<std::string, std::string> string_map; std::map<char, char> char_map; std::map<short, short> short_map; std::map<int, int> int_map; std::map<Fivebytes, Fivebytes> five_map; std::map<long, long> long_map; std::map<Longs2, Longs2> longs2_map; std::map<Longs3, Longs3> longs3_map; std::cout << "string: " << string_map.max_size() << std::endl; std::cout << "char(1): " << char_map.max_size() << std::endl; std::cout << "short(2): " << short_map.max_size() << std::endl; std::cout << "int(4): " << int_map.max_size() << std::endl; std::cout << "five(5): " << five_map.max_size() << std::endl; std::cout << "long(8): " << long_map.max_size() << std::endl; std::cout << "longs2(16): " << longs2_map.max_size() << std::endl; std::cout << "longs3(32): " << longs3_map.max_size() << std::endl; return 0; }
出力結果
> string: 384307168202282325 > char(1): 461168601842738790 > short(2): 461168601842738790 > int(4): 461168601842738790 > five(5): 384307168202282325 > long(8): 384307168202282325 > longs2(16): 288230376151711743 > longs3(32): 230584300921369395
括弧内はそれぞれの要素のbyte数.実際には,同じ型の要素2つをペアとしているので,その2倍の容量にしています.で,結果を見てみたのですが,どうも大きすぎる.今使ってるマシンのメモリが250G以上あることを考えてもちょっと多すぎないか.うーん,なんでだろうか.ひとまず今日のところは保留にする.(保留のままかもしれないが笑
2012/01/22 追記 --------------------------------
コメントで教えていただいたが,Pythonの辞書型とC++のstd::mapはデータ構造が違うらしい.勝手な思い込みできっと同じなんだろうと思ってた...気を付けなければ!
あと,今思えばstd::mapが木構造であることを考えれば,出力した際にkeyの順番で表示されるのももっともな気がする.
とにかく,コメントに感謝><