Perl
配列

Perlの配列初期化を検証してみた

このあいだ現場の秘蔵のスパゲッティを見たところ、配列初期化の書き方が僕が考えるコードと違っていました。
しかし例によって 何故か 動いていました。
この手の話は大体「Perlだから」で片付くのですが、せっかくなので実際の挙動を自宅環境で検証してみました。

一応ラクダ本とか掘りながら書いてみてはいますが、認識に誤りがあったら教えてくださいませ。

環境:perl 5, version 16, subversion 3 (v5.16.3)

冗長なので割愛していますが、頭にこちらのおまじないをつけております。
また、出力されたエラーメッセージの行数もみやすいように編集しております。

use strict;
use warnings;
use utf8;
use Data::Dumper;

結論

参照($)配列は要素の型を「ある程度」参考にするので、明示的に配列ではない場合はエラーを吐きます。

実体(@)配列は何が来たところで要素に追加していって無理やり配列にします。
指定を間違えると間違えた指定が配列要素にされてしまいます。

検証

参照($)配列、実体(@)配列ともに初期値を下記パターンに設定した後、
"hogehoge"をPushしてDumperし、挙動の違いを見ていきます。

パターン

  1. undef()
  2. []
  3. ()
  4. ""
  5. {}

参照配列の初期化

undef()で初期化

未定義状態だとどう動くか検証。

array_ref.pl
my $array_ref_undef = undef();
push (@{$array_ref_undef}, "hogehoge");
print Dumper($array_ref_undef)."\n";
print $array_ref_undef."\n";

$VAR1 = [
          'hogehoge'
        ];

ARRAY(0x9682b8)

$である以上変数か配列か定義がないとまずいかなとも思ったが、
@$でPushしているのもあり、問題なく動作する。

参考までに
push ($array_ref_undef, "hogehoge");
はNot an ARRAY referenceを吐いて動かなくなる

()で初期化

実体配列で定義してみる。

array_ref.pl
my $array_ref_round_brancket = ();
push (@{$array_ref_round_brancket}, "hogehoge");
print Dumper($array_ref_round_brancket)."\n";
print $array_ref_round_brancket."\n";

$VAR1 = [
          'hogehoge'
        ];

ARRAY(0x131d918)

普通に参照配列になってました。意味が分からない。
ちなみに職場で動いてたのもこのパターンでした。

[]で初期化

何が普通かもわからなくなってきましたが、リファレンス配列初期化の標準例と思われるコードを記載。

array_ref.pl
my $array_ref_brancket = [];
push (@{$array_ref_brancket}, "hogehoge");
print Dumper($array_ref_brancket)."\n";
print $array_ref_brancket."\n";
$VAR1 = [
          'hogehoge'
        ];

ARRAY(0x131d2b8)

""で初期化

空文字列で定義した場合どうなるのかなと。

array_ref.pl
my $array_ref_quoted = "";
push (@{$array_ref_quoted}, "hogehoge");
print Dumper($array_ref_brancket)."\n";
print $array_ref_brancket."\n";

Can't use string ("") as an ARRAY ref while "strict refs" in use at inittest.pl line 2.

これは流石に怒られました。

空ハッシュで初期化

array_ref.pl
my $array_ref_curly_brancket = {};
push ($array_ref_curly_brancket, "hogehoge");
Not an ARRAY reference at inittest.pl line 2.

無理ですよね知ってます。

実体配列の初期化

同様にテスト。
挙動としては「実体配列の空」以外を指定した場合は問答無用で1個目の要素に設定されます。

undef()で初期化

array_test.pl
my @array_undef = undef();
push (@array_undef, "hogehoge");
print Dumper(@array_undef)."\n";
print @array_undef."\n";
$VAR1 = undef;
$VAR2 = 'hogehoge';

2

()で初期化

array_test.pl
my @array_round_brancket = ();
push (@array_round_brancket, "hogehoge");
print Dumper(@array_round_brancket)."\n";
print @array_round_brancket."\n";
$VAR1 = 'hogehoge';

1

[]で初期化

array_test.pl
my @array_brancket = [];
push (@array_brancket, "hogehoge");
print Dumper(@array_brancket)."\n";
print @array_brancket."\n";
$VAR1 = [];
$VAR2 = 'hogehoge';

2

「配列の中に参照配列がある」って状態になりますね、これ。
要するに多次元配列になるわけですね。
中の配列にアクセスしたい場合は

push (@array_brancket[0],"fugafuga");

途中でこのコードを挟むと

$VAR1 = [
          'fugafuga'
        ];
$VAR2 = 'hogehoge';

2

こうなります。

実体配列なので、
@{$array_brancket->[0]}みたいな書き方をすると通りません。

""で初期化

array_test.pl
my @array_quoted = "";
push (@array_quoted, "hogehoge");
print Dumper(@array_quoted)."\n";
print @array_quoted."\n";
$VAR1 = '';
$VAR2 = 'hogehoge';

2

空ハッシュで初期化

array_test.pl
my @array_curly_brancket = {};
push (@array_curly_brancket, "hogehoge");
print Dumper(@array_curly_brancket)."\n";
print @array_curly_brancket."\n";
$VAR1 = {};
$VAR2 = 'hogehoge';

2

あとがき

私の検証は以上です。
漏れているパターン等あったら是非教えてください。

ハッシュの中に入れたらどうなるのか、ってのもあるのですが、
現状のパターンからある程度推測可能なのと、冗長になるので記載を避けています。

しかし、perlの考え方からすれば合理的な仕様だな、とは思いますが、
これを人に説明しきれる自信が全くありませんね、これ。
実装上も危ないポイントがいっぱいありそうなので、気を付けるようにします。