Perl日記

PerlとかRubyとかPHPとかPythonとか

カッコを含む文字列を置換するところではまった話と別解とのベンチマーク

すごく長い文字列から、最初にでてきた指定文字を削除したかったのだけど、
その指定文字に開きカッコだけか、閉じカッコだけが入っているとエラーで死んだ。

my $str = q!1qaz2wsx(3edc4rfv5tgb)6yhn7ujm!;
my $del = q!4rfv5tgb)6yhn!;
$str =~ s/$del//;
Unmatched ) in regex; marked by <-- HERE in m/4rfv5tgb) <-- HERE 6yhn/ at - line 3.

ああキャプチャのカッコの対応関係だと思って死んだのね、と思って、$delをエスケープしてあげたら動いてくれた。

my $str = q!1qaz2wsx(3edc4rfv5tgb)6yhn7ujm!;
my $del = q!4rfv5tgb)6yhn!;
$del =~ s/([\(\)])/\\$1/g;
$str =~ s/$del//;
#=> 1qaz2wsx(3edc7ujm

あれ確かこんなことしてくれる関数なかったっけ?、と思い出したら
quotemeta()だった。

my $str = q!1qaz2wsx(3edc4rfv5tgb)6yhn7ujm!;
my $del = q!4rfv5tgb)6yhn!;
$del = quotemeta($del);
$str =~ s/$del//;
#=> 1qaz2wsx(3edc7ujm

ていうかこんなことしてくれる特殊文字みたいなのなかったっけ?、と思い出したら
\Qだった。

my $str = q!1qaz2wsx(3edc4rfv5tgb)6yhn7ujm!;
my $del = q!4rfv5tgb)6yhn!;
$str =~ s/\Q$del\E//;
#=> 1qaz2wsx(3edc7ujm

つーかそもそも指定文字切り取るのに正規表現とか無駄じゃね?、と思ったので
index()とsubstr()の左辺値使ってみた。

my $str = q!1qaz2wsx(3edc4rfv5tgb)6yhn7ujm!;
my $del = q!4rfv5tgb)6yhn!;
substr($str, index($str, $del), length($del)) = ""; # lvalue!!
#=> 1qaz2wsx(3edc7ujm


TMTOWTDIにもほどがある!
よろしい、ならばベンチマークだ!

ベンチマーク結果

Benchmark: timing 1000000 iterations of \Q, quotemeta, s///, substr...
        \Q:  2 wallclock secs ( 1.56 usr +  0.00 sys =  1.56 CPU) @ 641025.64/s (n=1000000)
 quotemeta:  1 wallclock secs ( 1.57 usr +  0.00 sys =  1.57 CPU) @ 636942.68/s (n=1000000)
      s///:  6 wallclock secs ( 7.40 usr +  0.00 sys =  7.40 CPU) @ 135135.14/s (n=1000000)
    substr:  2 wallclock secs ( 2.11 usr +  0.00 sys =  2.11 CPU) @ 473933.65/s (n=1000000)
              Rate      s///    substr quotemeta        \Q
s///      135135/s        --      -71%      -79%      -79%
substr    473934/s      251%        --      -26%      -26%
quotemeta 636943/s      371%       34%        --       -1%
\Q        641026/s      374%       35%        1%        --

\Qとquotemetaがほぼ同じか。内部的に同じことしてそうだからかな。
substrがs///より早い、というより、正規表現2回使うことのコストが大きいって感じかな。
quotemetaとかより貧弱なことしてるのに。

結論

周りに正規表現大スキーが多いなら「\Q」、そうでないなら見た目に優しい「quotemeta」を使うようにしよう。