読者です 読者をやめる 読者になる 読者になる

Perlでは存在しない配列要素にアクセスするだけで自動的に生成されるから注意した方が良いよ

Perlを使っていて、大分衝撃的な事象を発見したので記しておきます。
自分が無知なだけかも知れませんが。

事象

Perlでは、例えば次のように書くことで自動的に配列が拡張されます。

my @e;
$e[5] = "hoge";
print "Array ".$#e." : ".join(", ", @e)."\n";

実行すると、

Array 5 : , , , , , hoge

となります。
予想通りです。私ですら知っています。あたりまえ体操ですね。

が、次のように変えて、

my $e = [];
eval { @{$e}[5]->isa("MyClass"); };
print "Array ".$#{$e}." : ".join(", ", @{$e})."\n";

実行すると、

Array 5 : , , , , , 

となります。
えぇ~!? マジで!? ふざけんじゃねーよぉぉぉ!!!

と、夜通しデバッグした私は朝日に向かって叫んだのでした。

配列の要素があり、かつ期待通りのオブジェクトがどうか、を確認したい箇所があり、どうせevalするし、配列サイズは確認しなくていーや。
と、軽い気持ちで処理を追加していたのですが、徹夜する羽目になるとわ。

検証

そして、いくつかパターンを検証してみると、どうも配列のリファレンスの場合に、メソッド呼び出しした時だけのようです。

my @e = ();
eval { $e[5]->isa("MyClass"); };
print "E is ".$#e." : ".join(", ", @e)."\n";

my $f = [];
eval { my $hoge = @{$f}[5]; };
print "F is ".$#{$f}." : ".join(", ", @{$f})."\n";

my $g = [];
eval { @{$g}[5]->hoge; };
print "G is ".$#{$g}." : ".join(", ", @{$g})."\n";

my $h = [];
eval { @{$h}[5]->isa("MyClass"); };
print "H is ".$#{$h}." : ".join(", ", @{$h})."\n";

これを実行すると、

E is -1 : 
F is -1 : 
G is 5 : , , , , , 
H is 5 : , , , , , 

となりました。

環境

CygwinPerlです。

/tmp # perl -v

This is perl 5, version 14, subversion 2 (v5.14.2) built for cygwin-thread-multi-64int
(with 7 registered patches, see perl -V for more detail)

Copyright 1987-2011, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.

/tmp #