$` は $& の前方の文字列、$' は $& の後方の文字列が保持される特殊変数です。よりエレガントな方法がありそうな気がしますがとりあえず。
…ということで、連休中に少しコードを弄ってみた結果のまとめ。
#!/usr/bin/perl use strict; my $content = "123abc456abc789abc"; my $counter = 1; while ( $content =~ /\d{3}/g ) { if ( $counter == 2 ) { $content =~ s/($`)$&($')/$1xyz$2/; } $counter++; } print $content;
元のコード original.pl では、$` と $' を用いて文字列全体に渡って置換が行われている( 8 行目)ため、動作パフォーマンスが気になるところ。そこで、e モディファイヤを利用したコードが sample.pl です。
#!/usr/bin/perl use strict; my $content = "123abc456abc789abc"; my $counter = 1; $content =~ s/\d{3}/ if( $counter++ == 2 ) { 'xyz'; } else { $&; } /eg; print $content;
置換後の文字列を e モディファイヤを使用して、指定された回数目の置換処理( 7 行目)の時だけ、目的の文字列 xyz
( 8 行目) を返しています。それ以外ではマッチした文字列($&)をそのまま返しており、マッチした部分がマッチした同じ文字列で置換されるため、結果、置換されていないように見えます。
対象の文字列長を変化させて動作パフォーマンスを計測してみました*1。
#!/usr/bin/perl use strict; use Benchmark; sub generate_content { join '', '123abc456abc789abc' x $_[0]; } sub Original { my $content = generate_content( $_[0] ); my $counter = 1; while ( $content =~ /\d{3}/g ) { if ( $counter++ % 2 ) { $content =~ s/($`)$&($')/$1xyz$2/; } } } sub MyCode { my $content = generate_content( $_[0] ); my $counter = 1; $content =~ s/\d{3}/ if ( $counter++ % 2 ) { 'xyz'; } else { $&; } /eg; } # http://search.cpan.org/~rjbs/perl-5.18.4/lib/Benchmark.pm Benchmark::cmpthese( 100_000, { Original => sub { Original( 1 ); }, MyCode => sub { MyCode( 1 ); }, }); Benchmark::cmpthese( 10000, { Original_10 => sub { Original( 10 ); }, MyCode_10 => sub { MyCode( 10 ); }, }); Benchmark::cmpthese( 100, { Original_100 => sub { Original( 100 ); }, MyCode_100 => sub { MyCode( 100 ); }, }); Benchmark::cmpthese( 100, { Original_50 => sub { Original( 50 ); }, Original_100 => sub { Original( 100 ); }, Original_500 => sub { Original( 500 ); }, }); Benchmark::cmpthese( 100, { MyCode_100 => sub { MyCode( 100 ); }, MyCode_1K => sub { MyCode( 1_000 ); }, MyCode_10K => sub { MyCode( 10_000 ); }, MyCode_100K => sub { MyCode( 100_000 ); }, MyCode_1M => sub { MyCode( 1_000_000 ); }, });
元のコードから、奇数個目のマッチを置換する処理に置き換えました。それぞれの関数(Original
と MyCode
)は、引数によって走査する文字列長さを違えることができます。文字列長さを変化させ、それぞれの置換処理に要する時間を計測した結果は次の通りです。
Rate Original MyCode Original 42194/s -- -78% MyCode 188324/s 346% -- Rate Original_10 MyCode_10 Original_10 2195/s -- -94% MyCode_10 33784/s 1439% -- Rate Original_100 MyCode_100 Original_100 50.9/s -- -99% MyCode_100 6250/s 12187% -- Rate Original_500 Original_100 Original_50 Original_500 2.42/s -- -95% -99% Original_100 51.3/s 2019% -- -71% Original_50 178/s 7253% 247% -- Rate MyCode_1M MyCode_100K MyCode_10K MyCode_1K MyCode_100 MyCode_1M 0.369/s -- -90% -99% -100% -100% MyCode_100K 3.76/s 919% -- -90% -99% -100% MyCode_10K 38.2/s 10252% 916% -- -90% -99% MyCode_1K 400/s 108427% 10552% 948% -- -94% MyCode_100 6250/s 1695631% 166338% 16281% 1463% --
MyCode
は、Original
の 4 倍以上高速である。MyCode
は、文字列長さが n 倍になると、処理速度はほぼ n 倍悪化する。 ⇒ Original
は、文字列長さが n 倍になると、処理速度はほぼ n2 倍悪化する。 ⇒ MyCode
の方がより高速に動作する。