SCALAR
(指定しなければ $_
)について、
それが次に変更される前に多くのパターンマッチをあらかじめ行う。
検索を行うパターンの性質や数、検索される文字列中の文字の頻度分布により
時間が節約されるかもしれないし、されないかもしれない。
多分これを使用するのとしないのと、
どちらが早く実行できるかを比較してみたいであろう。
数多くの短い定数文字列(より複雑なパターン中の定数部を含む)について
検索を行うループにおいて最も効果があるだろう。
同時に 1 つしか study
できない。
別のスカラーを study
すると、
最初に study
した方は `unstudied' になってしまう。
(study
の動作は次のようになる:
検索対象の文字列の全文字の linked list を作り、これにより、例えば、
全ての `k' という文字がどこにあるかがわかる。
C プログラムおよび英文から得た統計的な頻度テーブルに基づいて、
各検索文字列から最も稀な文字を選ぶ。
この"最も稀な"文字を含む位置のみを調べるのである。)
例えば、次のループは、あるパターンを含む行の前に、
インデックスを含むエントリを出力するものである。
while (<>) {
study;
print ".IX foo\n" if /\bfoo\b/;
print ".IX bar\n" if /\bbar\b/;
print ".IX blurfl\n" if /\bblurfl\b/;
...
print;
}
/\bfoo\b/
を検索する時は、
$_
中の `f' の位置のみを調べる。
なぜなら、`f' は `o' よりも稀だからである。
一般的に、特殊な場合を除いてこれは効果がある。
唯一の問題は、これにより最初に linked list を作るのにかかった以上に
時間を節約できるかどうかである。
注意:
実行時まで決定できない文字列を検索する場合は、
ループを全部文字列としてそれを eval
することで
何回も全パターンをコンパイルし直すのを防ぐことができる。
これと合わせて、$/
を全ファイルが 1 レコードになるように undef すると、
大変速く、専用のプログラム fgrep
よりも速いことが多い。
以下の例では ファイル群(@files
) から単語群(@words
) を検索し、
マッチするファイルの名前を出力する。
$search = 'while (<>) { study;';
foreach $word (@words) {
$search .= "++\$seen{\$ARGV} if /\b$word\b/;\n";
}
$search .= "}";
@ARGV = @files;
undef $/;
eval $search; # これは大変(this screams)
$/ = "\n"; # 通常の delimiter に戻す
foreach $file (sort keys(%seen)) {
print $file, "\n";
}