MeCabのJuliaバインディングMeCab.jlを作りました

Juliaから日本語形態素解析器として最も有名なMeCabを使えるMeCab.jlを作りました。

まだ、METADATA.jlにマージされていないのですが、きっと明日には使えるようになっていると思います。
[2014/09/15 23:33追記] マージされました!

How to use

簡単な使い方は

Pkg.add("MeCab")

を一度していただければ、こんな感じでアクセスできます

using MeCab mecab = Mecab() results = parse(mecab, "すももももももももものうち") for result in results println(result.surface, ":", result.feature) end

JuliaでCのコードをbindingするには

基本的には、公式マニュアルを読めばいいです。

Calling C and Fortran Code — Julia Language 0.4.0-dev documentation

とかだけ書くと大分辛いのですが、ポイントはccallを使えば良いということです。

例えば、以下の様なCのコードがあったとします。(マニュアルより引用)

int main(int argc, char \*\*argv);

すると、Julia側のコードはこう書けばよいのです。

argv = ["a.out", "arg1", "arg2"] ccall(:main, Int32, (Int32, Ptr{Ptr{Uint8}}), length(argv), argv)

問題は、ポインタが帰ってくる場合どうすればいいのかです。
これは、意外と簡単で第二引数をPtr{Void}で受けてあげれば良いです。

返り値が構造体なんかの場合は、対応するimmutableをJuliaで作ってあげて、unsafe_loadすれば良さそうです。

なお、関数ポインタも受けれるとか。
C Structs with function pointers - Google グループ

ただ未確認ですが、構造体のメンバに構造体がいる場合はどうやればいいのかわかりませんでした。
(なので、今回はmecab_node_tは諦めた)

コンストラクタとデストラクタ

MeCabのtaggerの様にに、Cで確保したポインタを保持しておく場合、コンストラクタで作りデストラクタで解放するのが良いようです。

bicycle1885さんのこの記事を参考に、実装してみました。
Juliaのデストラクター - りんごがでている

type Mecab ptr::Ptr{Void} function Mecab(option::String = "") argv = split(option) if(length(argv) == 0) argv = [""] end ptr = ccall( (:mecab\_new, "libmecab"), Ptr{Void}, (Cint, Ptr{Ptr{Uint8}}), length(argv), argv ) if ptr == C\_NULL error("failed to create tagger") end smart\_p = new(ptr) finalizer(smart\_p, obj -\> ccall((:mecab\_destroy, "libmecab"), Void, (Ptr{Void},), obj.ptr)) smart\_p end end

ポイントはfinalizerを実装すると、それがデストラクタとして働くということです。

C++は?クラスとかnamespaceとかは?

どうも扱えないようです。
色々と調べたのですが、特にnamespace周りは鬼門のようです。

なので、extern Cをしながらwrapperを一枚書くのが良さそうですね。

最後に

思ったより簡単にバインディングができました。
多分、Kytea.jlも書けそう。

この内容をJuliaTokyo #2でLTしてこようと思います。

Avatar
Aki Ariga
Machine Learning Engineer

Interested in Machine Learning, ML Ops, and Data driven business.