検索Slackの会話を元に一句詠む2015年02月15日SlackでBotに一句詠ませるために、r7kamura/ikkuという一句抽出ライブラリと、これを利用したr7kamura/ruboty-kokodeikkuというRuboty用プラグインをつくりました。様子このようにチャット上にBotを置いておくと、会話に反応して一句詠んでくれます。ruboty-kokodeikkuruboty-kokodeikkuは、チャットBot用のフレームワークであるRubotyのためのプラグインです。Botが参加している部屋の全ての発言を監視し、一句として妥当なパターンがあれば一句詠んでくれます。使い方は、mecabを使えるようにしてruboty-kokodeikkuをGemfileに追加するとOKです。Herokuで利用する場合は、以下のように環境変数を設定すると良いでしょう。Qiitaを参考にheroku-buildpack-multiを利用して上手くMeCabを入れたかったのですが、上手くいかなかったので今回はheroku-buildpack-mecabを利用してMeCabを使えるようにしました。Qiitaを読むと良いでしょう。メソッドでパターンを登録できます。指定してBOTにmentionを送らなくても反応するようにしました。Rubotyは全てのプラグインをGemfileで管理するため、プラグインをつくるにはGemをつくる必要があります。最初は、簡単に試すためGistにGemを置いていました。Qiitaに書いています。```Ikkuの導入方法r7kamura/ikkuを使うための準備について説明します。MeCabIkkuでは内部でMeCabというオープンソースの形態素解析エンジンを利用しています。そのため、MeCabとその辞書データが利用できる環境になっている必要があります。MacでHomebrewを利用している場合には、以下のようにインストールできます。mecab-ipadicRubyIkkuはRubyを利用して記述しました。2.0.0以上を必要とします。MeCabのRuby用ライブラリであるNattoを利用しています。RubyではなくNattoを利用したのは、インストールの難易度の低さを優先したためです。Ikkuの使い方r7kamura/ikkuのライブラリの使い方を説明します。文字列から一句を抽出するなどの目的で利用する場合は、このクラスのインスタンスを生成し、メソッドを呼んで文字列を与えることになります。ように引数を与えることでこれを変更することもできます。7])Ikku::Reviewer#judge(text)与えられた文字列が一句かどうかを判定し、trueまたはfalseを返すメソッドです。与えられた文字列中に一句が含まれているかどうかではなく、与えられた文字列全てを消費して一句詠めているかどうかを判定します。falseIkku::Reviewer#find(text)与えられた文字列の中から一句を探し、最初に見つかったものを返します。見つからなかった場合はnilを返します。ちなみにIkku::Reviewerは一句批評家です。ここで返却される一句を表すオブジェクトは、複雑ですが、Ikku::Nodeのインスタンスの配列の配列です。以下の例では、"古池"と"や"が五七五の最初の五を表す部分であり、二つのIkku::Nodeから構成されることを示しています。メソッドを呼ぶとひと繫ぎの文字列が得られます。"音"]]```Ikku::Reviewer#search(text)与えられた文字列の中に含まれる全ての一句を探し、それらの配列を返却します。findメソッドでは一つ見つかった瞬間に結果を返していましたが、このメソッドでは処理を継続して全ての一句の可能性を探索します。"春"]]]```Ikkuの仕組みIkkuの内部動作について説明します。Ikku::Reviewer一句レビュアーです。Ikku::Parserを使って与えられた文字列をIkku::Nodeの配列に変換し、Ikku::Scannerを使って一句を探索します。Ikku::Parser一句パーサーです。与えられた文字列を形態素解析エンジンにかけ、Ikku::Nodeの配列に変換します。MeCabで文字列を解析すると各形態素を表すNodeの配列が得られますが、Ikku::NodeはそれらのNodeをそれぞれ内包したものです。言語パーサで言うところの字句解析器のような働きをします。コメント部分を表す字句が廃棄される言語処理系があるように、Ikku::Parserではこの段階で不要なノードを廃棄します。文頭や文末のようなメタ情報を表すノードは廃棄しています。Ikku::Scanner一句スキャナーです。与えられたIkku::Nodeの配列を先頭から消費していって、一句が成り立つかどうかを判定します。言語パーサで言うところの構文解析器のような働きをします。先頭から消費できなければ失敗で、例えば「ああ古池や蛙飛び込む水の音ああ」のような文字列が与えられた場合に、このScannerでの判定は失敗します。「古池や蛙飛び込む水の音ああ」を表すIkku::Nodeの配列が与えられなければいけないわけです。一句を抽出する際、Ikku::Reviewerでは、Parserから得られたIkku::Nodeの配列をもとに様々な組み合わせをつくり、それぞれScannerに与えて判定させます。Ikku::Reviewer#findに「ああ古池や蛙飛び込む水の音ああ」が渡されたとき、Reviewerは以下のパターンをつくってそれぞれScannerに判定させます。ああfindでは一つ見つけた場合に処理を終えますが、searchではその後も処理を継続し、全てのパターンを探索します。searchでは「古池や蛙飛び込む水の音ああ」を表す配列が与えられた場合でも、五七五が取り出せた段階で成功ということになりますが、judgeでは完全に一句かどうか知りたいという要求があります。いうオプションが用意されており、judgeではこのオプションが利用されます。Ikku::Node一句ノードです。個々の形態素を表しています。MeCabから得られた品詞などの情報を簡単に扱えるように処理をラップしている他、モーラのような文節単位で音数を計算する処理なども担当しています。古池は四音節、河童は三音節ということになり、これを五七五の計算に利用します。自身が一句の先頭や末尾のノードとしてふさわしいかや、五・七・五の各句の先頭のノードとしてふさわしいかどうか、という情報を返す役割も担っています。接尾辞(-さ、-っぽい、-的など)を各フレーズの先頭に配置するのはふさわしくない、などです。これらの細かなルールづくりの違いが、句を句として認識する際の差異に繋がります。この辺の個人によって趣向のことなる事柄については処理を分離し、Ikkuはあくまで静的に解析できる情報を提供するに留め、利用者が外部からルールベースを与えられるようにできると良いですね。おわりr7kamura/ikkuという一句抽出ライブラリと、それを利用したr7kamura/ruboty-kokodeikkuというRuboty用プラグインについて紹介しました。一晩でガッと書いたので雑な仕組みになってるとは思いますが、まあまあ動くし便利という感じにはなったかと思います。完璧な一句というものが存在しないように、完璧な判定器というものもまたアレなわけですので、各位Ikkuを参考に君だけの最強の判定器をつくっていきましょう。あわせて詠みたいhttps://twitter.com/kokodeikku_bot。