作者: Bruce.
日時: 2006/6/12(18:12)
Bruce.です。

直接メールしようかとも考えましたが、ここならまつもとさんもZnZさんも
読んでいたと思いますし、最近ここも投稿がないので枯れ木も山の賑わい
ということで。

日記につらつらと何日かにわたって書いたことですが、発端は

% ruby -le 'p "bar".gsub(/\w??/){"[#{$&}]"}'
"[]b[]a[]r[]"
% perl -le '$_="bar";s/\w??/[$&]/g;print'
[][b][][a][][r][]

のように、RubyとPerlで動作が違うというのを見かけたことです
(ちなみにPythonはRubyと一緒でした)。

簡単にまとめると、

Perlのこの仕様は意図的なものです。perlreに、

高レベルループは繰り返しの間で付加的な状態:
最後のマッチが長さゼロかどうか、を保持します。
 ループから抜けるには、
長さゼロのマッチに後続するマッチでは長さゼロのマッチを禁止します。
この禁止はバックトラッキングと相互作用するので、 
最善(best)のマッチが長さゼロであった場合には 
次善(second best)のマッチが選択されます。

For example:

    $_ = 'bar';
    s/\w??/[$&]/g;


この結果は [][b][][a][][r][]となります。
貪欲でない(non-greedy) ??
によって与えられる最善のマッチの各位置は長さゼロのマッチとなり、
次善 (second best)のマッチは \wにマッチとなります。
したがって、長さゼロのマッチは長さ1のマッチによって置き換えられます。

という記述があります。
#サンプル例からするとZnZさんは既にこれをご存知?

で、一方Rubyですが、これもソースコードにコメントつきで

	if (BEG(0) == END(0)) {
	    /*
	     * Always consume at least one character of the input string
	     * in order to prevent infinite loops.
	     */
	    if (RSTRING(str)->len <= END(0)) break;
	    len = mbclen2(RSTRING(str)->ptr[END(0)], pat);
	    memcpy(bp, RSTRING(str)->ptr+END(0), len);
	    bp += len;
	    offset = END(0) + len;
	}

というのがありますので、意図的な動作です。

RubyKaigiの懇親会でまつもとさんにお訊きすることができたので確認したところ、
「Perlの動作をお手本に決めたと思う」とのことでした。ただその後
確認したところでは 5.005_04でも現在の仕様と同じでしたので、まつもとさんが
参考にしたというのはさらに遡るのではないかと思われます。

で、簡単にPerlの現在の挙動について説明したところ、「同じ動作にするのは
無理だろうね」ということらしいです。

あと忘れないように書いておきますが、純粋な空文字列を渡したときの
処理の流れは微妙に違うようです。>Perl

時間がなくてPythonの実装のソースコードによる確認はまだしていません。

いじょ。