Regexp::Trieにさわってみた
にわかににぎわっているはてなキーワードを高速に付与なのですが、dankogaiさんのRegexp::Trieをちょっとさわってみた。
Trieを利用したRegexpのオプティマイズという理解で間違っていないですよね。
#キーワードリスト
my @src = qw(1U 2ch amazon apache apple atom blog cdbi CentOS cgiapp colinux cpan csrf css dashboard db firefox flash foaf ftp google hacker hard httpd intrablog ipod);
my $rt = Regexp::Trie->new();
map{$rt->add($_)} @src;
my $regexp = $rt->as_regexp;
print $regexp,"?n";
とすると、
(?-xism:(?:1U|2ch|CentOS|a(?:mazon|p(?:ache|ple)|tom)|blog|c(?:dbi|giapp|olinux|pan|s(?:rf|s))|d(?:ashboard|b)|f(?:irefox|lash|oaf|tp)|google|h(?:a(?:cker|rd)|ttpd)|i(?:ntrablog|pod)))
こういう正規表現が得られる。使う時は、
#キーワードの前後に「"」をつける $text =~ s/$regexp/"$&"/go;
これのソースをみていて、やっぱりすげぇなぁと思った。
sub add{
my $self = shift;
my $str = shift;
my $ref = $self;
for my $char (split //, $str){
$ref->{$char} ||= {};
$ref = $ref->{$char};
}
$ref->{''} = 1; # { '' => 1 } as terminator
$self;
}
なにげないように見える上のコードなんだけど。
この部分だけで動くようにしてみると、
$ perl -e '
> use Data::Dumper;
> my $self = {};
> my $ref = $self;
> foreach my $char (split //,"perl"){
> $ref->{$char} ||= {};
> $ref=$ref->{$char};
> }
> $ref->{""}=1;
> print Dumper($self);
> '
$VAR1 = {
'p' => {
'e' => {
'r' => {
'l' => {
'' => 1
}
}
}
}
};
こうなる。
これがなぜこうなるのかがすぐに分からなくて悩んだ。$refだけを見ているとわからない。リファレンスと$selfの動きをちゃんと追って行けばよかった。
こんな作り方(コード)ができるようになりたい。