Go で Mecab を使ってみた

$GOROOT/misc/cgo/ にサンプルがあったので、試してみた。
Makefile は $GOROOT/misc/cgo/stdio/Makefile を流用して、
バインド部分を書いて、make するだけ。


実際に動かしてみたい場合は、検証用のソースコードを用意して、
ファイル名が main.go なら make main のように実行すると、実行ファイルができあがる。


作ってみて気づいた点は、

  • 作成したライブラリは ${GOROOT}/pkg/${GOOS}_${GOARCH} に置かれる。
  • cgo で生成された .cgo3.c と .cgo4.c の関数名の頭に 0xC2 が挿入される。
  • パッケージ名称に、Makefile の TARG や元ファイルの file.go が影響する。

かな。


カテゴリは Go と golang のどっちにしようかな。

Mecab バインド用ソースコード

package mecab

/*
#include <mecab.h>

struct mecab_t {};
*/
import "C"

func New2(s string) *C.mecab_t {
    p := C.CString(s);
    m := C.mecab_new2(p);
    return m;
}

func SparseToStr(m *C.mecab_t, s string) string {
    p := C.CString(s);

    r := C.mecab_sparse_tostr(m, p);

    ret_val := C.GoString(r);

    return ret_val;
}

Makefile

include $(GOROOT)/src/Make.$(GOARCH)

TARG     = mecab
CGOFILES = file.go

#CGO_CFLAGS  = -I
#CGO_LDFLAGS =
CGO_LDFLAGS  = -lmecab

include $(GOROOT)/src/Make.pkg

%: install %.go
        $(GC) $*.go
        $(LD) -o $@ $*.$O

example:
        @export LD_LIBRARY_PATH=/usr/local/lib;  \
        ./main

動作検証用ソースコード

package main

import "fmt"
import "mecab"

func main() {
    fmt.Printf("hello, world\n");

    s := "太郎は次郎が持っている本を花子に渡した。";

    m := mecab.New2("");

    r := mecab.SparseToStr(m, s);

    fmt.Printf("result: \n%s\n", r);
}

実行結果

$ make example
result:
太郎    名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
次郎    名詞,固有名詞,人名,名,*,*,次郎,ジロウ,ジロー
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
持っ    動詞,自立,*,*,五段・タ行,連用タ接続,持つ,モッ,モッ
て      助詞,接続助詞,*,*,*,*,て,テ,テ
いる    動詞,非自立,*,*,一段,基本形,いる,イル,イル
本      名詞,一般,*,*,*,*,本,ホン,ホン
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
花      名詞,一般,*,*,*,*,花,ハナ,ハナ
子      名詞,接尾,一般,*,*,*,子,コ,コ
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
渡し    動詞,自立,*,*,五段・サ行,連用形,渡す,ワタシ,ワタシ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。      記号,句点,*,*,*,*,。,。,。
EOS

CentOS5.4 に OpenCV 2.0 をインストールする

rpm パッケージではなく、ソースコードからコンパイルしてインストールします。
既に 2.0.0 がリリースされているので、今回はそれで。

0. 準備

以下のサイトから OpenCVソースコードをダウンロードする。
http://sourceforge.net/projects/opencvlibrary/

画像処理で使うライブラリのパッケージをインストールする。

# yum install libpng libpng-devel
# yum install libtiff libtiff-devel
# yum install zlib zlib-devel

動画処理を行うなら、ffmpeg もあったほうがいいのかな。yum で見つからなかったから、今回は無視しようっと。

1. コンパイル & インストール

$ tar jxf OpenCV-2.0.0.tar.bz2
$ cd OpenCV-2.0.0
$ ./configure
$ make
# make install

Lua から C の関数をコールする - C に Lua を組み込む

Lua側から呼び出す関数名と呼び出される関数を lua_regster() で登録する。
すると、登録した名前で Lua から呼び出せるようになります。

今回は、Lua から受け取った引数の数とそれぞれの型を出力しています。
スタック操作のインデックスは 1 からなので注意。

ソースコード

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

int lua_argument_type(lua_State *L);

int main (int argc, char *argv[])
{
    lua_State *L;

    if (argc < 2) {
        fprintf(stderr, "usage: %s filename\n", argv[0]);
        exit(1);
    }

    L = luaL_newstate();

    luaL_openlibs(L);

    // Lua側から呼び出す関数名と呼び出される関数を登録する。
    lua_register(L, "argument_type", lua_argument_type);

    if (luaL_dofile(L, argv[1])) {
        fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
        lua_close(L);
        exit(1);
    }

    lua_close(L);

    exit(0);
}
	
int lua_argument_type(lua_State *L)
{
    int n, i, type;

    // 引数の数(スタックにある要素の数)を取得する
    n = lua_gettop(L);

    printf("number of arguments: %d\n", n);

    for (i = 0; i < n; i++) {
        // インデックスの開始は 1 から
        type = lua_type(L, i + 1);
        printf("  %d) %s(%d)\n", i + 1, lua_typename(L, type), type);
    }

    return 0;
}

Lua コード

t = {};

function foo (x, y)
  return  x + y;
end

argument_type("a", false, 20, t, nil, foo);

実行結果

number of arguments: 6
  1) string(4)
  2) boolean(1)
  3) number(3)
  4) table(5)
  5) nil(0)
  6) function(6)

スタックの内容を出力する C 関数を作成する - C に Lua を組み込む

スタックの内容を一つずつ lua_type() で型を取得して、値を出力する関数を作成します。
lua_isnumber() や lua_isstring()、lua_istable() の関数やマクロを使って、if else で場合わけもできます。
引数の型がテーブルの場合、key と value の型だけを出力しています。

ソースコード

#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

int lua_argument_dump(lua_State *L);

int main (int argc, char *argv[])
{
    lua_State *L;

    if (argc < 2) {
        fprintf(stderr, "usage: %s filename\n", argv[0]);
        exit(1);
    }

    L = luaL_newstate();

    luaL_openlibs(L);

    // Lua側から呼び出す関数名と呼び出される関数を登録する。
    lua_register(L, "argument_dump", lua_argument_dump);

    if (luaL_dofile(L, argv[1])) {
        fprintf(stderr, "error: %s\n", lua_tostring(L, -1));
        lua_close(L);
        exit(1);
    }

    lua_close(L);

    exit(0);
}
	
int lua_argument_dump(lua_State *L)
{
    int n, i, type;

    n = lua_gettop(L);

    for (i = 0; i < n; i++) {
        // インデックスの開始は 1 から
        type = lua_type(L, i + 1);

        switch(type) {
        case LUA_TNIL:
            printf("  %d) nil\n", i + 1);
            break;
        case LUA_TNUMBER:
            printf("  %d) %f\n", i + 1, lua_tonumber(L, i + 1));
            break;
        case LUA_TBOOLEAN:
            printf("  %d) %d\n", i + 1, lua_toboolean(L, i + 1));
            break;
        case LUA_TSTRING:
            printf("  %d) %s\n", i + 1, lua_tostring(L, i + 1));
            break;
        case LUA_TTABLE:
            printf("  %d) %s\n", i + 1, lua_typename(L, type));

            lua_pushnil(L);
            while (lua_next(L, i + 1) != 0) {
                printf("    %s - %s\n",
                    lua_typename(L, lua_type(L, -2)), lua_typename(L, lua_type(L, -1)));
                lua_pop(L, 1);
            }

            break;
        case LUA_TFUNCTION:
        case LUA_TUSERDATA:
        case LUA_TTHREAD:
        case LUA_TLIGHTUSERDATA:
            printf("  %d) %s\n", i + 1, lua_typename(L, type));
            break;
        default:
            break;
        }
    }

    return 0;
}

Lua ソース

t = {
  name  = "foo",
  value = 10,
};

function foo (x, y)
  return  x + y;
end

argument_dump("a", false, 20, t, nil, foo);

実行結果

  1) a
  2) 0
  3) 20.000000
  4) table
    string - string
    string - number
  5) nil
  6) function

RubyGems をユーザ権限の環境にインストールする

rugygems をユーザ権限のディレクトリにインストールします。
わざわざ root ユーザにならなくていいし、管理が楽かなっと。


手順は以下のサイトを参考にしました。


$HOME/opt/gems に RubyGems を、
$HOME/opt/gems_repository に RubyGemsリポジトリを、インストールしています。
今回使用するユーザは hoge
Ruby のバージョンは 1.8.5 (2006-08-25) [i386-linux]
RubyGems のバージョンは 1.3.5

1. RubyGems をダウンロードする

$ wget http://rubyforge.org/frs/download.php/60718/rubygems-1.3.5.tgz

2. ダウンロードしたファイルを解凍する

$ tar zxf rubygems-1.3.5.tgz

3. インストールする

$ cd rubygems-1.3.5
$ export GEM_HOME=$HOME/opt/gems_repository
$ ruby setup.rb --prefix=$HOME/opt/gems
RubyGems 1.3.5 installed

=== 1.3.5 / 2009-07-21

Bug fixes:

* Fix use of prerelease gems.
* Gem.bin_path no longer escapes path with spaces. Bug #25935 and #26458.

Deprecation Notices:

* Bulk index update is no longer supported (the code currently remains, but not
  the tests)
* Gem::manage_gems was removed in 1.3.3.
* Time::today was removed in 1.3.3.


------------------------------------------------------------------------------

RubyGems installed the following executables:
        /home/hoge/opt/gems/bin/gem

4. インストール後の設定

きちんとインストールされていると、$HOME/opt/gems/bin に gem があるはずです。
ただ、この状態で実行してもエラーになります。
これからパスとライブラリのパスを通していきます。

$ $HOME/opt/gems/bin/gem
/home/hoge/opt/gems/bin/gem:8:in `require': no such file to load -- rubygems (LoadError)
        from /home/hoge/opt/gems/bin/gem:8

gem コマンドのあるパスとライブラリのあるパスを設定します。
環境変数に設定するだけだと、次にログインしたとときに反映されないので、
.bashrc に環境変数を設定します。

$ vi ~/.bashrc
# configuration: rubygems
export PATH=$PATH:$HOME/opt/gems/bin:$HOME/opt/gems_repository/bin
export RUBYLIB=$HOME/opt/gems/lib
export GEM_HOME=$HOME/opt/gems_repository
$ . ~/.bashrc

パスが通っていれば、以下のコマンドを実行すると RubyGems のバージョンが表示されます。

$ gem -v
1.3.5

ためしに RSpec をインストールしてみる

RSpec をインストールして、実際にライブラリとコマンドが使えるかどうかを確認しています。

$ gem install rspec
ライブラリが使えるかを確認

以下のようなスクリプトを書いて、実行してみます。
ライブラリが require されて、エラーがでなければ OK です。

#!/usr/bin/ruby

require "rubygems"
require "spec"

... 省略 ...
コマンドが実行できるかを確認

RSpec のバージョン情報を表示してみます。

$ spec -v
rspec 1.2.9

終了

ユーザ環境の RubyGems ができました。
おしまい。

VMWare Player に CentOS5.4 をインストールしてみた

3.0 から VMWare Player でもディクスイメージを作れるようになったので、
CentOS5.4 をインストールしてみた。

インターネット越しのネットインストールは時間がかかるので、DVDイメージをダウンロードした。
ディスクに余裕があればこっちのほうがいいね。ダウンロード時間は 1h30m くらい。

パラメータはこんな感じで。30分くらいでインストールが完了。

ゲストOS: Linux
バージョン: CentOS
仮想マシン名: CentOS5.4
ネットワーク接続: ブリッジ

VMWare Player 3.0 は起動が遅い感じがする。ウインドウは表示されるけれど、反応がない時間が長い。
VirtualBox3.0は起動は早かったのに。

ホストは Windows7(32bit) だけれど、今のところは問題なく動作している。