Google日本語入力で素因数分解をする

Mozcとはなんぞ

Mozc とは Google 社が開発した日本語入力のオープンソース版の名称です。リリース版はGoogle日本語入力として公開されています。Google 社の工藤拓氏、小松弘幸氏の 20%プロジェクトか ら始まって正式プロジェクトに昇格し、昨年末にはとうとうベータ版から正式リリースになりました。しかしオープンソースにできない部分があるためか、Mozc と Google 日本語入力では機能が少し異 なる部分もあるようです。Google 日本語入力の開発版という位置付けでもあるので、ただ使うだ けなら Google 日本語入力のほうがいいかもしれません。あるいは研究のために改造するのもいいと思います。そのためのオープンソースでもありますので。

Google 日本語入力・Mozc ともにウェブ上の膨大なテキストから変換に必要な情報を生成しています。*1ウェ ブ上のテキストにはたくさんの誤字が含まれているため、例えば「危機一発」 などのよくある誤変換が新しい名詞として辞書登録されていたり*2、「雪歩」さんの読みが「ゆき」だったりといった残念な部分もあります。しかし Google 検索の「もしかして」機能を応用して補正をかけているので、そこまで多くの誤字は登録されていません。技術的な面の概要が知りたい場合は、ウェブで公開されている妙な漫画を見ると良いと思いま す。この漫画では Mozc の技術的な概要が一通り説明されています。
http://www.google.co.jp/intl/ja/ime/comic/

Build

私は何度やってもMacでMozcをビルドしようとするとはまります。QtGUIとかQtCoreとか。いつかそれについても詳しく書くかもしれません。GYPがよく分からないので、ビルドに詰まったところは、自分でコマンドを打ってコンパイルしました。

便利な機能の紹介

ローマ字テーブル設定

どのレイヤでやってるのか忘れましたが、辞書登録に似た機能を使って「who」と打つと「うぉ」、「lu」と打つと「ぅ」が入力できるなど、日本語入力システムごとに微妙に異なるローマ字変換のカスタマイズができます。
辞書登録機能と違うのは、置き換えした後も変換は確定していないというところです。これの便利な応用として zh, zj, zk, zl などの入力を、変換を確定させることなく←↓↑→に置き換えることができます。他にも、z.で…など。

おみくじ

「おみくじ」と打って変換してみてください。今日の運勢が出ます。私の今日の運勢は中吉でした。
別に便利でも何でもないですけど。

Calculator

Google日本語入力でも利用可能ですが、計算式を入力すると変換候補に、式を評価した結果が出てきます。しかし「人生、宇宙、すべての答え」は4342にならない*3

Rewriterというレイヤ

何がどうRewriteなのか知らないけど、とにかく変換候補を作るときに毎回呼ばれるようです。このレイヤでは、変換候補の順番を入れ替えたり、特殊な変換候補を動的に追加したりといったことが可能です。詳細は忘れました。どっかにメモった気がするんだけど。日付の変換や、おみくじ、Calculator、基数変換などはこのレイヤで実現されています。

Rewriterに機能を追加する

Calculatorを参考に、素因数分解を行うRewriterを書いてみました。書類仕事の最中に「この数字は素数だろうか?」と気になって仕事が手に付かなくなってしまうようなことは誰しもありますね。このRewriterはそんな悩みをその場で解決できる、最もエレガントで美しいツールです。
Calculatorを削るだけ削って、100行くらい書き足したらできました。だいたい以下のような感じです。

実行例

ソースコード

多分BSDライセンス。Calculatorを参考に作りました。また、Calculatorを作ったのはGoogleです。

// 素因数分解する
// \d+= の形になっていなかったら、ResizeSegmentする
bool FactorRewriter::Rewrite(Segments *segments) const {
	const size_t segments_size = segments->conversion_segments_size();
	if (segments_size == 0)
		return false;

	// If |segments| has only one conversion segment, try prime factorization and insert
	// the result on success.
	if (segments_size == 1) {
		const string &key = segments->conversion_segment(0).key();
		string result;
		if (!prime_factorization(key, &result))
			return false;
		// Insert the result.
		if (!Factor_InsertCandidate(result, 0, segments->mutable_conversion_segment(0)))
			return false;
		return true;
	}

	string merged_key;
	for (size_t i = 0; i < segments->conversion_segments_size(); ++i)
		merged_key += segments->conversion_segment(i).key();

	string result;
	if (!prime_factorization( merged_key, &result))
		return false;

	// Merge all conversion segments.
	ConverterInterface *converter = ConverterFactory::GetConverter();
	int offset = Util::CharsLen(merged_key) -
		Util::CharsLen(segments->conversion_segment(0).key());

	if (!converter->ResizeSegment(segments, 0, offset)) {
		LOG(ERROR) << "Failed to merge conversion segments";
		return false;
	}
	return true;
}

Segmentは文節と、その文節の変換候補(Candidateクラス)を所有するクラス。Segmentsはその配列を持ち、文節区切りを管理する……多分。

最後に

そのうち、wikipediaを使って変換候補に関する説明をつけたり、芸能人の名前を変換したらヒントにコンビ名を表示したりなんてことをやる予定。でも本当はもっと深いところの改造をしてみたい。

*1:追記 2011/03/24 Google日本語入力だけ

*2:もちろん、報告されているものは修正されています。検索結果とは違い、人間が手を加えてもいい情報なので。

*3:辞書登録じゃダメ。計算結果が4342にならないとダメ。