理系学生日記

おまえはいつまで学生気分なのか

look-ahead assertion を根本的に誤解していた

Mastering Perl 読んでたら,ぼくが look-ahead assertion を根本的に誤解していたことに気づいた!!
ISBN:978-0596527242:detail

なんか誤解してた!

look-ahead assertion は正規表現の先読み機能.
ぼくは look-ahead assertion を「ある正規表現 re1 にマッチし,かつマッチした文字列の次の文字列が正規表現 re2 のような正規表現にマッチする」かどうかを調べるときの re2 のために使うものだと思っていたのだけど,別にそうではないらしい!

間違えてたと気付いたのは,次のテストコードを実行してみたとき.

#!/opt/local/bin/perl
use strict;
use warnings;

$_ = "Here come Wilma and Fred!";
print "1" if /(?=.*Wilma).*Fred/;

$_ = "Here come Fred and Wilma!";
print "2" if /(?=.*Wilma).*Fred/;

$_ = "Here come Wilma and Fred!";
print "3" if /.*Fred(?=.*Wilma)/;

$_ = "Here come Fred and Wilma!";
print "4" if /.*Fred(?=.*Wilma)/;

print "\n";

この出力は "124" となる.
最初はそもそも,1 と 2 はぼくの look-ahead の理解だと意味のわからない使い方でした.ぼくの理解だと look-ahead assertion は "正規表現 re1 (ただし re1 の次の文字列は re2 にマッチする)" を探すために使うものなのに,なんで re2 が先にあるの?バカなの?死ぬの?って感じだった.

どうも

look-ahead assertion は,"anchor を付けるために使うもの" と考えると良さそうです.ここでの anchor の意味は,次にどの場所からマッチ処理を始めるかって意味で.
look-ahead assertion "(?=.*Wilma)" を見つけると,正規表現エンジンは

  • 現在位置からの文字列が,正規表現 ".*Wilma" にマッチするかどうかを調べる
    • マッチするようなら,マッチした直前の位置に anchor を打って,現在位置からマッチを続行
    • マッチしないようなら,正規表現マッチ処理を終了

てなるような挙動.

だから,1 とか 2 とかは,最初に ".*Wilma" にマッチするかを先読みして,マッチするから現在位置 ("Here" の直前) に戻って, ".*Fred" を探し直すという処理になる.
3 は ".*Fred" にマッチした後,"!" の直前から ".*Wilma" を探す.もちろん以後 "Wilma" は出現しないからから全体としてのマッチは失敗する.
4 は ".*Fred" にマッチした後," and" の直前から ".*Wilma" を探す.これはマッチするから全体としてのマッチも成功するってことだな!