namazu-ml(avocado)


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: chasen module



knok@xxxxxxxxxxxxx (Nokubi Takatsugu) wrote:

>  2723のファイルがある自分のメールフォルダを対象にした結果、従来の
>chasenコマンドを試用した場合には
>
>real    43m4.671s
>user    33m41.410s
>sys     7m29.480s
>
># 指定オプション: -c -h -a -O
>
>  とずいぶんかかったのに対し、モジュールを使用した場合は
>
>real    9m3.894s
>user    6m53.730s
>sys     1m47.670s
>
># 指定オプション: -c -p -h -a -O
># -pはモジュールを使用するためのフラグ
>
>  と、かなりの性能向上がありました。

すごい! 参考までに diff を貰えませんか? KAKASI のモジュール化に挑
戦してみたいです。

# KAKASI というより、わかち書き専門の高速なモジュールが欲しいです。
# どなたか作りませんか?


>  しかし、茶筅の作成元に連絡をとってみると、既にperlモジュールを作成し
>ている(ただし現在は非公開)とのことで、次のリリースにはそれを含めて公開
>されるとのことでした。

なるほど。楽しみです。

NKF の Perl モジュールも出たことですし、KAKASI/ChaSen もモジュール
化して外部コマンドを呼び出さないようにすれば mknmz のパフォーマン
スはかなり向上するはずです。

  1. 処理速度優先
     -> NKF, KAKASI/ChaSen を XS のモジュールにする
        ◯ 速くなる
        ◯ 外部コマンドが不要になる
        × Win32, OS/2 での動作は難しくなる
        × モジュールのインストールがちと面倒

  2. 簡単動作優先
     -> NKF, KAKASI/ChaSen の代わりを Perl で書く
        ◯ Perlだけで動く
        × 遅くなる

  3. 現状維持
     -> NKF, KAKASI/ChaSen は外部コマンド
        △ 効率はいまいち
        × 外部コマンドをインストールする必要がある

個人的には外部コマンドの呼び出しは好きではないので 1 + 2 の形にす
るのがよろしいかと思います。XSのモジュールがあればそれを使ってなけ
れば手製のルーチンを使うと (configureで識別?)。

しばらくは現状維持になると思いますが…。

-- Satoru Takabayashi

おまけ

わかち書き程度なら Perlで書いてもそこそこの速度は出せます。辞書の
読み込みにかかる overhead (かなりのものですが) を差し引けば KAKASI 
の 2〜3倍程度の時間で処理できます。

せっかくなのでいい加減に作ったスクリプトを添付しておきます。 \G と 
pos を使っているところがミソです。チューニングすればもっと速くなる
でしょう。


#!/usr/bin/perl
use strict;
use IO::File;
use NDBM_File;
my $CHAR  = "(?:[\x21-\x7e]|[\xa1-\xfe][\xa1-\xfe])";
my $NONKANJI = "(?:[\x21-\x7e]|[\xa1-\xaf][\xa1-\xfe])";
my $KIGOU = "(?:[\xa1-\xaf][\xa1-\xfe])";
my $CHOON    = "(?:[\xa1][\xbc])";
my $HIRAGANA = "(?:(?:[\xa4][\xa1-\xf3])|$CHOON)";
my $KATAKANA = "(?:(?:[\xa5][\xa1-\xf6])|$CHOON)";
my $KANJI    = "(?:[\xb0-\xfe][\xa1-\xfe])";

my %dict;
unless (defined($ARGV[0])) {
    print <<USAGE;
    usage: wakati <kakasidict>
    example: cat hoge.txt | wakati kakasidict > kekka.txt

USAGE
    exit 1;
}
STDIN->autoflush(1);
#init_dict($ARGV[0]);
print STDOUT "loading $ARGV[0]...\n";
load_dict($ARGV[0]);
print STDOUT "done.\n";
shift @ARGV;

main();

sub main() {
    my $content = join('', <STDIN>);
    my $pos = 0;
    my $leng = length($content);
    while (1) {
	if ($content =~ /\G($KANJI+)/g) {
	    print wakati($1), " ";
	} else {
	    pos $content = $pos;
	    if ($content =~ 
		/\G([\x21-\x7e]+|$HIRAGANA+|$KATAKANA+|$KIGOU+|\s+)/g) {
		print $1, " ";
	    } else {
		die "unkown error\n";
	    }
	}
	$pos = pos $content;
	last if $pos >= $leng;
    }
    print "\n";
}
#untie %dict;

sub load_dict($)
{
    my ($dictfile) = @_;
    my ($fh) = new IO::File;

    $fh->open("$dictfile") || die "$!: $dictfile\n";
    while (<$fh>) {
	next if /^;/;

	/^(.*?) +(.*)$/;
	if (defined($dict{$2})) { # conflict
#	    print STDERR "'$2 ($1)' is already defined as '$2 ($dict{$2})'!\n";
	    next;
	}
	$dict{$2} = 1;
    }
}

sub wakati($) {
    my ($hyoki) = @_;
    my $hyoki_rest = $hyoki;
    my @konkyo = ();
    my $max = length($hyoki);

    if (length($hyoki) <= 4) {
	return $hyoki;
    }
    while (length($hyoki_rest) > 0) {
	my $tmp = $hyoki_rest;
	my $try = "";
	my $hyoki_match = "";

	while (length($tmp) > 0) {
	    $tmp =~ /^($CHAR)/;
	    $try .= $1;
	    if (defined($dict{$try}) && length($try) <= $max) {
		$hyoki_match = $try;
	    } 
	    $tmp =~ s/^$CHAR//;
	}
	if (length($hyoki_match) > 0) {
	    $hyoki_rest =~ s/^$hyoki_match//;
	    push(@konkyo, $hyoki_match);
	    $max = length($hyoki);
	} elsif ($hyoki_rest =~ /^($NONKANJI+)/ &&
		 length($1) < $max) 
	{ 
	    my $hyoki_match = $1;
	    $hyoki_rest =~ s/^$hyoki_match//;
	    push(@konkyo, $hyoki_match);
	    $max = length($hyoki);
	} elsif (@konkyo > 0) {
	    my $tmp = pop(@konkyo);
	    $max = length($tmp);
	    $hyoki_rest = $tmp . $hyoki_rest;
	} else {
	    last;
	}
    }
    my $tmp = join(' ', @konkyo, $hyoki_rest);
    $tmp =~ s/\s+$//;
    return $tmp;
}

sub init_dict($) {
    my ($dictfile) = @_;

    unless (-f "$dictfile.pag") {
	tie %dict, "NDBM_File", "$dictfile", O_RDWR|O_CREAT, 0666 ||
	    die "can't open dbm!\n";
	load_dict("$dictfile");
    } else {
	tie %dict, "NDBM_File", "$dictfile", O_RDWR|O_CREAT, 0666 ||
	    die "can't open dbm!\n";
    }
}