namazu-ml(avocado)
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: chasen module
- From: Satoru Takabayashi <satoru@xxxxxxxxxxxxx>
- Date: Thu, 19 Nov 1998 02:02:19 +0900
- X-ml-name: namazu
- X-mail-count: 01621
- References: <199811181052.TAA20365@ns.eal.or.jp>
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";
}
}