燈明ブログ

現状は小池啓仁の応援ブログ

黒魔術を読み解く

久々に弾さんのブログを見て、以下のソースが理解できなかったので、調べてみました。
折角なので、調べたことをメモしときます。

#!/usr/bin/perl
use strict;
use warnings;
{
    no warnings 'redefine';
    *CORE::GLOBAL::glob = sub{
        my $expr = shift;
        warn $expr;
        $expr =~ s{
            \A(.*?)~~(.*?)(\^\.)(\.\^?)(.*?)
        }{
            my $num = eval $1;
            my $min = $2;
            my $lt0  = length($3) == 1 ? '<=' : '<';
            my $lt1  = length($4) == 1 ? '<=' : '<';
            my $max = $5;
            qq($min $lt0 $num && $num $lt1 $max);
        }emsx;
        warn $expr;
        eval $expr;
    };
}
local $\="\n";
print "OK" if     <length('^..^') ~~ 3^..^5>;
print "OK" unless <length('^..^') ~~ 0^..^3>;
http://blog.livedoor.jp/dankogai/archives/51222364.html

no warnings 'redefine';

関数を再定義する時のワーニングを抑制する。

*CORE::GLOBAL::glob = sub{

組み込みglob関数をオーバーライド(関数再定義)しています。

my $expr = shift;

glob関数の第1引数を$exprへ代入しています。

warn $expr;

標準エラー出力へ$exprの内容を出力しています。

$expr =~ s{ \A(.*?)~~(.*?)(\^\.)(\.\^?)(.*?) }{ ・・・ }emsx;

\Aは、常に文字列の先頭にのみマッチします。
ちなみに、^ では文字列中に埋め込まれた改行の直後にもマッチしてしまいます。
後の正規表現は、ちょっと問題あり・・・かも(最後の方で説明します)。

e修飾子

e修飾子は、s{}{} の右側を式とみなして実行します。

m修飾子

m修飾子は、文字列を複数行として扱います。

s修飾子

s修飾子は、ドット(.) が改行にも一致するようにします。

x修飾子

x修飾子は、パターン内のスペースを無視し、コメントを入れられるようにします。

my $num = eval $1;

今回は、length('^..^')の実行結果が$numに入ります。

my $min = $2;

今回は、3^..^5 の3が$minに入ります。

my $lt0 = length($3) == 1 ? '<=' : '<';

3項演算で、今回は3^..^5 の『^.』が$3入り、lengthは文字列『^.』なので2なり、$lt0には '<' が入ります。

my $lt1 = length($4) == 1 ? '<=' : '<';

3項演算で、今回は3^..^5 の『.^』が$4入り、lengthは文字列『.^』なので2なり、$lt1には '<' が入ります。

my $max = $5;

今回は、3^..^5 の5が入ります。

qq($min $lt0 $num && $num $lt1 $max);

文字列 "$min $lt0 $num && $num $lt1 $max" と等価です。
しかし、単なる文字列でどこにも代入していない。
たぶん、最後に評価したものが現状のもを置換して、$exprに格納されるのかな・・・たぶん。

warn $expr;

標準エラー出力へ$exprの内容を出力しています。

eval $expr;

今回は、$exprには、文字列 "$min $lt0 $num && $num $lt1 $max" が格納されていて、eval(実行)します。

local $\="\n";

print文の最後に"\n"が付加されるようです。

正規表現がおかしい?

調べていて気がついたのですが正規表現が間違っているような感じです。
\A(.*?)~~(.*?)(\^\.)(\.\^?)(.*?)

\A(.*?)~~(.*?)(\^\.|\.)(\.\^|\.)(.*)
ではないでしょうか?
3..5

3..^5
の場合に置換しないのです。

ネコ演算子(^..^)

そもそもネコ演算子は、Perl6で実装されるとのことで、Perl5の範囲演算子の機能拡張版です。
3..5は (3,4,5)のリスト
3^..5は、(4,5)のリスト
3..^5は、(3,4)のリスト
3^..^5は、(4)のリスト