燈明ブログ

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

Perlの謎(番外編3):『コンスタントと変換演算子のoverload』とは

先日紹介した、結城先生のPerlクイズの以下の回答が、さっぱり分からなかったので調べてみました。

use overload '""' => sub{${+shift}*$_*2}, '0+'=>sub{${+shift}};
BEGIN {
    overload::constant (integer => sub{my $a=shift; bless \$a, 'main'})
}
http://archive.mag2.com/0000015670/20030208004000000.html

そもそも、クイズの問題は、以下の通りです。

puzzle.plという1つのスクリプトの最後の部分を示します。
このスクリプトの(省略)と書かれている部分を補って、
Figに示す実行結果とぴったり同じになるようにしてください。

◆List:前の部分が省略されたスクリプトpuzzle.pl
(省略)

$puzzle = 0;
print "$puzzle, " for (1..10);
print "\n";
$puzzle = 1;
print "$puzzle, " for (1..10);
print "\n";
$puzzle = 2;
print "$puzzle, " for (1..10);
print "\n";
$puzzle = 3;
print "$puzzle, " for (1..10);
print "\n";

◆Fig:実行結果
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
4, 8, 12, 16, 20, 24, 28, 32, 36, 40,
6, 12, 18, 24, 30, 36, 42, 48, 54, 60,

http://archive.mag2.com/0000015670/20030208004000000.html

では、ネットやラクダ本で私が調べたことを書きますね。
(あまり理解できていないので間違っている可能性大です)

◆コンスタントのoverload

BEGIN {
    overload::constant (integer => sub{my $a=shift; bless \$a, 'main'})
}

これは、BEGINブロックなのでコンパイル時に実行されます。
まず、コンスタント(今回は整数値)のオーバーロードをしています。
ロジックで使用している整数値すべてをオブジェクトに変換しています。
つまり、ロジック上の整数値が見つかる毎にsubが起動され、整数値は第1引数に設定されるので、それをshiftで取得し、blessしてオブジェクトに変換しています。
なぜ、オブジェクトに変換しているかと言うと、オーバーロードは、オブジェクトに対して行われるからです。
つまり、$puzzleへ代入する整数値やforの範囲演算子の1や10をオブジェクトにしてオーバーロードを可能にしています。

◆『0+』変換演算子のoverload

use overload '""' => sub{${+shift}*$_*2}, '0+'=>sub{${+shift}};

『'0+'=>sub{${+shift}}』は、オブジェクトを数値コンテキストで評価した場合にsubが起動されます。
今回の場合は、forの範囲演算子の1と10の場合です。
『${+shift}』は、起動されたsubの第1引数に数値が渡され、それをshiftで取得し、デリファレンスし、数値としてリターン値にしています。尚、数値は範囲演算子の1と10です。
尚、単項演算子の『+』は、shiftを関数として認識させるためです。『+』がないとスカラー変数 $shift になってしまう。
つまり、今回は、オブジェクトの1と10を数値の1と10へ変換しています(BEGINブロック処理の逆)。

◆『""』変換演算子のoverload

『'""' => sub{${+shift}*$_*2}』は、"$puzzle, "のようにオブジェクトが格納されている$puzzleを文字列中で評価した場合にsubが起動されます。
『'0+'=>sub{${+shift}}』で、forの範囲演算子の1と10がオブジェクトから整数値になっているので、for文が実行されて、$_には、1から10が順次格納されます。


『${+shift}*$_*2』のshiftには、$puzzleの格納オブジェクトに対応した数値が返り、数値に対応したリファレンスをデリファレンスしてから、$_と2をかけてます。
つまり、$puzzle=0の時は0で、$puzzle=1の時は1が、『${+shift}』の返り値になります。


ということで、sub{${+shift}*$_*2}は、$puzzle=1の場合、以下のようになるわけです。


1*1*2 = 2
1*2*2 = 4
:
1*9*2 = 18
1*10*2 = 20


以上です。

オーバーロード(overload)とは

ちなみに、オーバーロードとは、OOP用語では「多重定義」のことで、普通、関数やメソッドを多重定義しますが、もともとOOPでないPerlでは、演算子オーバーロードしかできません。


また、演算子オーバーロードは、オーバーライドではないので唯一でなく、見た目は同じ演算子でも、オペランドのオブジェクトで演算子が定義された場合によって複数の意味を持ちます(多態性ポリモルフィズム)。


尚、上記今回のオーバーロードは、演算子といっても特殊な変換演算子("", 0+)とコンスタントのオーバーロードでした。


たぶん、今の実力では、おかしなことを書いている可能性が大です。
将来的には、気が付き次第、修正変更してきますね!