Perl5のサブルーチン呼び出し時に二項演算子を期待していたら単項演算子の引数として認識されてしまう
sub func { return 100; }
みたいなサブルーチンがあった時に,
say(func - 99);
みたいな感じで呼び出すと,パッと見 1
が出力されそうに見えるが,実際は 100
が出力される.
これは - 99
が func
の引数として解釈されて食われるためである. B::Deparse
するとわかりやすい.
$ perl -MO=Deparse func.pl sub func { use warnings; use strict; use feature 'say'; return 100; } use warnings; use strict; use feature 'say'; say func(-99); func.pl syntax OK
ここから,二項演算子としての振る舞いを期待していた -
が 99
と結合して単項演算子として扱われ, -99
が引数として扱われているのが読み取れる.
もちろん,サブルーチン呼び出し時にかっこを付けて
say(func() - 99);
という感じで呼び出してやれば当初の期待値である 1
が表示されるようになる.もしくはプロトタイプを利用する方法もある.
sub func () { return 100; }
このように引数を取らないことをプロトタイプで明示すると,サブルーチン呼び出し時にかっこを付けなくても後続の値が引数として解釈されることはなくなる.その場合の B::Deparse
の結果は以下のようになる.明解.
$ perl -MO=Deparse func.pl sub func () { use warnings; use strict; use feature 'say'; return 100; } use warnings; use strict; use feature 'say'; say func - 99; func.pl syntax OK
これらは基本的な挙動ではあるのだけれど,日々を生きているとたまにハマりがち.例えば Time:Piece
と Time::Seconds
をあわせて使ったときなんかに
use Time::Piece; use Time::Seconds qw/ONE_HOUR/; my $t = localtime - ONE_HOUR; say $t;
などと書こうものなら,結果が Thu Jan 1 08:00:00 1970
というふうになり破滅する.localtime(-ONE_HOUR)
となるからだ.
普段,perlの組み込み関数の時は (基本的に) かっこを付けずに記述するスタイルで書いていたので,組み込み関数である localtime
をその感覚で使っていたところ綺麗にハマってしまった. *1
いままでは割と野生の勘みたいな感じでこのへんの諸問題を回避していて,「そういう掟なのじゃ」という感じで生活していたんですが真面目に B::Deparse
とかで調べてみると色々学びがあるものですね.
*1:Time::Pieceはlocaltimeをoverrideするから最早組み込み関数ではないんでは? という意見はあると思いますが