スクリプト言語のバインディング

$Id: bindings.html,v 1.9 2004/08/06 18:05:13 taku-ku Exp $;

各種スクリプト言語 (perl, ruby, python, Java) から, MeCab が提供する形態素解析の機能を利用可能です. 各バインディングは SWIG というプログラ ムを用いて, 自動生成されています. SWIG がサポートする他の言語も 生成可能だと思われますが, 現在は, 作者の管理できる範囲内ということで, 上記の4つの言語のみを提供しております.


インストール

各言語バイディングのインストール方法は, perl/README, ruby/README, python/README, java/README を御覧下さい.

とりあえず解析する

MeCab::Tagger というクラスのインスタンスを生成し, parse (もしくは parseToString) というメソッドを呼ぶことで, 解析結果が文字列として取得できます. MeCab::Tagger のコンストラクタの引数は, 基本的に mecab の実行形式に与え るパラメータと同一で, それらを配列として表現します. ただし, perl の場合は, 配列のリファレンスを与えます.

この配列は, C の main 関数に与える引数と 同じ構造でなければなりません. 配列の第一要素は, プログラム名等を入れてください. (実際には, 第一引数はダミーで, 使われることはありません).

各形態素の詳細な情報を取得する

MeCab::Tagger クラスの, parseToNode という メソッドを呼ぶことで, 「文頭」という特別な形態素が MeCab::Node クラスのインスタンスとして 取得できます.

MeCab::Node は, 双方向リストとして表現されており, next(), prev() というメソッド があります. それぞれ, 次の形態素, 前の形態素を MeCab::Node クラスのインスタンスとして 返します. 全形態素には, next () メソッドを順次呼ぶことでアクセスできます.

MeCab::Nodeクラスには, hasNode () というメソッドがあります. 返り値が 0 の場合は, このインスタンスには, 形態素情報が保持されておりません. 文末の形態素の次の形態素は, 0 が返ります.

以下に perl の例を示します. この例では, 各形態素を順次にアクセスし, 形態素の表層文字列, 前の品詞と現在の品詞, その形態素までのコストを表示し ます.

 use MeCab;
 my @arg = ($0, "-Ochasen");
 my $m = new MeCab::Tagger (\@arg);
 my $n = $m->parseToNode ("今日もしないとね"); # MeCab::Node のインスタンスが返る

 while ($n->hasNode () != 0) {
   printf ("%s\t%s-%s\t%d\n",
            $n->getSurface(),          # 表層
	    $n->prev()->getFeature (), # ひとつ前の品詞
	    $n->getFeature(),          # 現在の品詞
	    $n->getTotalCost ()        # その形態素までのコスト
	    );
   $n = $n->next (); # 次に移動
 }

形態素ラティス(Lattice)にアクセスする(上級者編)

ある Node (便宜上 rnode と表現)について, rnode.prev() は常に定義でき, 最適な 1つ前の Node を返します. しかし, 一般には, rnode に接続する 1つ前の node (便宜上 lnode と表現) は, 複数存在します.

MeCab::Path クラスは, lnode と, lnode が rnode に接続した場合, rnode.getTotalCost() が返すべきコストの2つの情報を保持するクラスです.

MeCab::Path は, 単方向リストで表現されています. まず, rnode.getPath () を呼ぶことで, Path のうち, 1つが取得できます. さらに, MeCab::Path クラスには, next() というメソッドがあり, rnode に接続する「別の」 Path インスタンスを返します. 単方向リストであるため, next() を順次に呼ぶことで, すべての Path を 取得することができます. また, MeCab::Path クラスには, hasPath () というメソッドがあります. 返り値が0 の場合は, このインスタンスには, Path が保持されておりません. hasPath () の戻り値を用いて, すべての path を巡回しかたどうか判定できます.

MeCab::Path クラスには, さらに, getNode () 及び getCost () メソッドがあります. getNode() は, rnode に接続する lnode であり, getCost()は, , lnode が rnode に仮に接続した場合の rnode.getTotalCost() が返すべきコストになります.

rnode に接続する複数の Path のそれぞれについて, getCost () を呼び, それらの最小値が,rnode が持つべきコスト (rnode.getTotalCost()) になります. また, rnode.prev() の返り値は, 最小値を出力した Path が保持する Node となります.

一般に Path をすべて保持するには 多くのメモリを必要とし, 解析に時間がかかります. MeCab は高速性を犠牲にしたくないために, デフォルトの動作では, Path を保持せず, 最適解しか求めません. そのため, Path を展開したい場合は, MeCab を -a オプション付きで 起動する必要があります.

以下の例は, 最適解とのコストの差が 8000 点以下の Node を深さ優先で探索し, 表示する例です.

#!/usr/bin/perl

use MeCab;

my %Node;

sub search ()
{
    my $rnode = shift @_;
    my $rid   = $rnode->getID();        # 右 Node の ID
    my $bcost = $rnode->getTotalCost(); # 右 Node の 最適コスト
    $Node{$rid} = $rnode;                  # 解析したという印を付ける

    # 右 Node に接続するすべての Path について..
    for (my $p = $rnode->getPath (); $p->hasPath(); $p = $p->next()) {
        next if ($p->getCost() - $bcost > 8000);  # 解析幅以下のものみを残す
        my $lnode = $p->getNode();
        my $lid   = $lnode->getID();
        next if (defined $Node{$lid}); # 印がついてるものは無視
        &search ($lnode);  # lnode を新しい親にして, 再帰
    }
}


my $m = new MeCab::Tagger ([$0, "-a"]); # -a をつける, 全探索を行なう.
my $n = $m->parseToNode ("今日もしないとね"); 

# いったん 文末まで捜査する
while ($n->next()->hasNode()) { $n = $n->next(); }

# 深さ優先で, 後ろから前へ探索
&search ($n);

for my $id (sort {$a <=> $b} keys %Node) {
    print $Node{$id}->isBest() ? "* " : "  ";  # Best の場合は, * を表示する
    print $id, "\t", $Node{$id}->toString(), "\n";
}

エラー処理

もし, コンストラクタや, 解析途中でエラーが起きた場合は, RuntimeError 例外が発生します. 例外のハンドリングの方法は, 各言語のリファレンスマニュアルを ごらんください. 以下は, python の例です

try:
    m = MeCab.Tagger (sys.argv)
    print m.parse ("今日もしないとね")
except RuntimeError, e:
    print "RuntimeError:", e;

注意事項

全メソッド

以下に, SWIG用のインタフェースファイル の一部を示します. バイディングの実装言語の都合上, C++ のシンタックスで 表記されていますが, 適宜読みかえてください. また, 各メソッドの動作も添え ていますので参考にしてください.

  class Tagger {
  public:
    // コンストラクタ, スクリプト言語からは, 文字列の配列として与える.
    Tagger (int argc, char *argv[]);

    // 文字列を解析し, 解析結果を文字列として得る.
    char* parseToString (char *);

    // 文字列を解析し, 先頭の形態素(Node)を返す
    Node parseToNode (char *);

    // parseToString () と同じ
    char* parse (char *);

    // NBest 解の出力
    char *parseNBest(int N, char*)

    // 解析結果を確からしいものから順番に取得する場合の初期化
    int parseNBestInit (char *)

    // 次に確からしい解析結果を文字列として取得. 
    // parseNBestInit () を実行しておく必要がある
    int next ();

    // 次に確からしい解析結果の先頭の形態素(Node) を返す
    // parseNBestInit () を実行しておく必要がある
    Node nextNode()

    // mutex を使い, インスタンスを排他的に lock する 
    // C インタフェースの mecab_lock と同じ
    int lock ();

    // lock を解除する
    // C インタフェースの mecab_unlock と同じ
    int unlock ();
  };

  class Path {
    // 指している Path 実体が使用可能の場合は, 1 それ以外は 0 を返す
    int hasPath ();

    // 次の Path を返す
    Path next ();
 
    // このパスを通ったときの 文頭からの最適コストを返す
    unsigned int getCost ();

    // Path が保持する Node を返す
    Node getNode ();
  }

  class Node {
  public:
    // 指している Node 実体が使用可能の場合は, 1 それ以外は 0 を返す
    int hasNode ();

    // 最適パス上にある形態素の場合は, 1 それ以外は 0 を返す
    int isBest();

    // 1つ前の Node を返す
    Node prev ();

    // 1つ先の Node を返す
    Node next ();

    // この Node に接続する, 左側の Path の 1つを返す
    Path getPath ();

    // 文字列表現として取得する. (default の mecab の出力と同一)
    char* toString ();

    // 形態素の表層文字列を取得する
    char* getSurface ();

    // 形態素に付与される素性 (品詞,活用,読みなど) を CSV 形式で取得する
    char* getFeature ();

    // 形態素の表層の文字列の長さを取得する
    unsigned int getLength ();

    // 形態素の開始位置を取得する.
    unsigned int getBegin ();

    // 形態素の終了位置を取得する.
    unsigned int getEnd ();

    // 形態素に付与された一意の ID を取得する
    unsigned int getID ();

    // 形態素の前々件の状態番号を取得する
    unsigned int getRcAttr2 ();

    // 形態素の前件の状態番号を取得する
    unsigned int getRcAttr1 ();

    // 形態素の後件の状態番号を取得する
    unsigned int getLcAttr ();

    // 品詞 ID を 取得する
    unsigned int getPOSID ();

    // この形態素までのコストを取得する
    unsigned int getTotalCost ();

    // 前形態素との連接コストを取得する
    unsigned int getConnectionCost ();

    // 単語生起コストを取得する
    unsigned int getWordCost ();

    // 単語生起コスト + 連接コストの和 (形態素コスト) を取得する
    unsigned int getNodeCost ();
  };

サンプルプログラム

perl/test.pl, ruby/test.rb, python/test.py, java/test.java にそれぞれの言語のサンプルがありますので, 参考にしてください.


$Id: bindings.html,v 1.9 2004/08/06 18:05:13 taku-ku Exp $;

taku-ku@is.aist-nara.ac.jp