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してこようと思います。