C++で単語の登場回数を数える

昨日Rでおわたになっていたので、C++でリベンジマッチをするべく準備を進める。

#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <mecab.h>

using namespace std;

vector<string> split( string s, string c ){
  vector<string> ret;
  for( int i=0, n; i <= s.length(); i=n+1 ){
	n = s.find_first_of( c, i );
	if( n == string::npos ) n = s.length();
	string tmp = s.substr( i, n-i );
	ret.push_back(tmp);
  }
  return ret;
}

typedef map<string, int>::const_iterator map_freq_it; //下のみたいに毎回型を書いているのが面倒なときに別名を付けることができるのがtypedef
typedef std::vector<map_freq_it>::const_iterator vec_stu_citer_t;

bool compare( const map_freq_it& a, const map_freq_it& b ){ // ファンクタというらしい
  return ( a->second > b->second );
}

int main (int argc, char **argv) 
{
  ifstream fin("/Users/yasuhisa/dbcls/txt/2297_51_2006.txt");
  string str;
  char c;
  while (fin.get(c)) {
	str.push_back(c); // 文字列の連結
  }
  
  MeCab::Tagger *tagger = MeCab::createTagger( "" );
  const MeCab::Node *node = tagger->parseToNode( str.c_str() );
  map<string, int> freq;
  
  map<string, int>::iterator it;

  for( node=node->next; node->next; node=node->next ){ 
	vector<string> strvec = split( node->feature, "," );
	if (strvec[0] == "名詞"){
	  string noun = strvec[6];
	  it = freq.find(noun);
	  if (it != freq.end()){
		it->second += 1;
	  }else{
		freq.insert(pair<string, int>(noun, 1));
	  }
	}
  } 
 
  vector<map_freq_it> sorted;
  for(map_freq_it mfi = freq.begin(); mfi != freq.end(); ++mfi)
	sorted.push_back(mfi);

  sort(sorted.begin(), sorted.end(), compare);

  for(vec_stu_citer_t it = sorted.begin(); it != sorted.end(); ++it){
	cout << (*it)->first << ",";
	cout << (*it)->second <<endl;
  }
  delete tagger;
  return 0;
}

結果を見てみる。

/Users/yasuhisa/cpp% ./mecab | head -n 30
*,3062
細胞,243
極性,90
分子,59
制御,58
こと,54
シグナル,52
体,50
転写,48
分裂,46
因子,46
変異,45
発現,45
経路,37
側,35
非対称,33
移動,32
局在,32
図,30
機能,27
方向,27
化,27
よう,25
軸,25
索,24
ニューロン,24
核,23
関与,23
位置,22
割,21

苦労したところ、ちょっと身についたところ

難しい、とかではなくC++らしいやり方とかそもそもまだそんなにC++をしゃべれないとかなところだけど。

  • 文字列のsplitがなくて困った
    • RubyとかPerlみたいに付属じゃない
    • boostを使うとどうにかできるとかできないとか
  • typedefが何者かちょっと分かった
    • 「map::const_iterator」みたいな型を毎回書くのは面倒なので、この型の別名を付けてあげよう、というのがtypedefであると理解した
  • const_iteratorというのがあるのを覚えた
    • がいまいちまだ分かっていない
  • compareの「const map_freq_it&」の部分でなんでconstと「&」が必要なのかが分かっていない
  • 日本語のファイルを読むところから苦労した
    • 一文字ずつ読んだりしていて、マルチバイトがぶっこわれてしまっている、というところも気づいていなかった。id:mickey24に助けてもらった
  • MeCabで形態素解析された特定のフィールドを取ってくる方法が分からなかった
    • ベクトルのindexを指定しているんだけど、構造体っぽくどうにかならないのかなあ
  • mapのvalueでsortする方法がうまいこといかなかった
  • あと分かりやすかったので、C++クックブックを購入

あとboostのインストール

この前はソースから自分で入れたんだけど、なんか今日はmacportで入れてみた。というわけで入っている場所が違う。

/opt/local/include/boost% ls
accumulators			date_time.hpp			iostreams			pointer_cast.hpp		statechart
algorithm			detail				is_placeholder.hpp		pointer_to_other.hpp		static_assert.hpp
aligned_storage.hpp		dynamic_bitset			iterator			pool				static_warning.hpp
any.hpp				dynamic_bitset.hpp		iterator.hpp			preprocessor			strong_typedef.hpp
archive				dynamic_bitset_fwd.hpp		iterator_adaptors.hpp		preprocessor.hpp		system
array.hpp			dynamic_property_map.hpp	lambda				program_options			test
asio				enable_shared_from_this.hpp	last_value.hpp			program_options.hpp		thread
asio.hpp			exception			lexical_cast.hpp		progress.hpp			thread.hpp
assert.hpp			exception.hpp			limits.hpp			property_map.hpp		throw_exception.hpp
assign				exception_ptr.hpp		logic				property_map_iterator.hpp	timer.hpp
assign.hpp			filesystem			make_shared.hpp			proto				token_functions.hpp
bimap				filesystem.hpp			math				ptr_container			token_iterator.hpp
bimap.hpp			foreach.hpp			math_fwd.hpp			python				tokenizer.hpp
bind				format				mem_fn.hpp			python.hpp			tr1
bind.hpp			format.hpp			memory_order.hpp		random				tuple
blank.hpp			function			mpi				random.hpp			type.hpp
blank_fwd.hpp			function.hpp			mpi.hpp				range				type_traits
call_traits.hpp			function_equal.hpp		mpl				range.hpp			type_traits.hpp
cast.hpp			function_output_iterator.hpp	multi_array			rational.hpp			typeof
cerrno.hpp			function_types			multi_array.hpp			ref.hpp				units
checked_delete.hpp		functional			multi_index			regex				unordered
circular_buffer			functional.hpp			multi_index_container.hpp	regex.h				unordered_map.hpp
circular_buffer.hpp		fusion				multi_index_container_fwd.hpp	regex.hpp			unordered_set.hpp
circular_buffer_fwd.hpp		generator_iterator.hpp		next_prior.hpp			regex_fwd.hpp			utility
compatibility			get_pointer.hpp			non_type.hpp			scoped_array.hpp		utility.hpp
compressed_pair.hpp		gil				noncopyable.hpp			scoped_ptr.hpp			variant
concept				graph				nondet_random.hpp		serialization			variant.hpp
concept_archetype.hpp		implicit_cast.hpp		none.hpp			shared_array.hpp		vector_property_map.hpp
concept_check			indirect_reference.hpp		none_t.hpp			shared_container_iterator.hpp	version.hpp
concept_check.hpp		integer				numeric				shared_ptr.hpp			visit_each.hpp
config				integer.hpp			operators.hpp			signal.hpp			wave
config.hpp			integer_fwd.hpp			optional			signals				wave.hpp
crc.hpp				integer_traits.hpp		optional.hpp			signals.hpp			weak_ptr.hpp
cregex.hpp			interprocess			parameter			smart_cast.hpp			xpressive
cstdint.hpp			intrusive			parameter.hpp			smart_ptr.hpp
cstdlib.hpp			intrusive_ptr.hpp		pending				spirit
current_function.hpp		io				pfto.hpp			spirit.hpp
date_time			io_fwd.hpp			pointee.hpp			state_saver.hpp

環境設定のところはちゃんと変えてあげる必要があるでせう。

Effective Modern C++ ―C++11/14プログラムを進化させる42項目

Effective Modern C++ ―C++11/14プログラムを進化させる42項目