dorapon2000’s diary

忘備録的な。セキュリティとかネットワークすきです。

Perlのautovivificationをいろいろ実験

Modern Perlを読んでいたら、autovivificationという機能があるということを今更ながら知った。たいへん便利だ。。。癖が強いらしいので手を動かして挙動を確かめてみる。

autovivificationとは

my %users;
$users{Brad}{id} = 228;
$users{Jack}{id} = 229;

$users{Brad}はまだ定義されていない(undef)はずだが、$users{Brad}{id}で代入できている。これはperl$users{Brad}を自動生成しているからで、autovivificationと呼ぶ。

perldocにもしっかりと説明がある。 autovivification

実験

Perl 5.30.2で行った。

use Data::Dumper;

sub func_array {
    my @h = @_;
    return @h;
}

sub func_ref {
    my ($h) = @_;
    return $h;
}

{
    # ハッシュ
    my %h;
    $h{empty}{aaa} = 'CCC';
    $h{not_empty}{aaa} = 'ZZZ';
    if (undef $h{undeff}{aaa}) {}
    if (exists $h{exists}{aaa}) {}
    func_array($h{via_func}{aaa});
    warn Dumper(\%h);
}

{
    # ハッシュリファレンス
    my $h;
    $h->{empty}->{aaa} = 'CCC';
    $h->{not_empty}->{aaa} = 'ZZZ';
    if (undef $h->{undeff}->{aaa}) {}
    if (exists $h->{exists}->{aaa}) {}
    func_ref($h->{via_func}->{aaa});
    warn Dumper($h);
}

{
   # 宣言と代入が同じ場合
    my $h->{aaa}->{bbb} = 'CCC';
    warn Dumper($h);
}

出力結果

$VAR1 = {
          'exists' => {},
          'undeff' => {
                        'aaa' => undef
                      },
          'via_func' => {},
          'empty' => {
                       'aaa' => 'CCC'
                     },
          'not_empty' => {
                           'aaa' => 'ZZZ'
                         }
        };
$VAR1 = {
          'not_empty' => {
                           'aaa' => 'ZZZ'
                         },
          'empty' => {
                       'aaa' => 'CCC'
                     },
          'via_func' => {},
          'undeff' => {
                        'aaa' => undef
                      },
          'exists' => {}
        };
$VAR1 = {
          'aaa' => {
                     'bbb' => 'CCC'
                   }
        };
  • リファレンスであろうと、なかろうと、宣言と同時に代入しようとautovivificationはされた
  • 参考のQiitaの記事でも取り上げられているように、undef関数でもautovivificationされた
  • キーの存在確認をするexistsですらautovivificationされた
  • undefとexistsでは生成のされ方が少し違うので注意
  • 関数に存在しないキーで引数として渡す際にもautovivificationされたが、最後までは生成されなかった?

no autovivification

参考で上げているQiitaの記事ではautovivificationというプラグマモジュールでautovivificationをいい感じに無効化していたため、インストールして使ってみた。

$ cpanm autovivification
# この一行を先程のコードの先頭に追加
no autovivification;
# no autovivification qw/ fetch exists delete /;と同じ

出力結果

$VAR1 = {
          'empty' => {
                       'aaa' => 'CCC'
                     },
          'via_func' => {},
          'undeff' => {
                        'aaa' => undef
                      },
          'not_empty' => {
                           'aaa' => 'ZZZ'
                         }
        };
$VAR1 = {
          'undeff' => {
                        'aaa' => undef
                      },
          'empty' => {
                       'aaa' => 'CCC'
                     },
          'via_func' => {},
          'not_empty' => {
                           'aaa' => 'ZZZ'
                         }
        };
$VAR1 = {
          'aaa' => {
                     'bbb' => 'CCC'
                   }
        };

existsだけautovivificationが無効化された。metacpanに書かれているが、何も指定せずno autovivificationした場合、rvalueデリファレンス$h->{a}->{b}が右側)・exists・deleteでautovivificationが無効化されるようだ。storeを指定すればlvalueデリファレンス$h->{a}->{b}が左側)でも対応できる。

no autovivification qw/  fetch exists delete store /;

storeを指定すると上で示したコードは実行できないコードとなるので、適宜修正する必要がある。もし実行した場合、以下のようなエラーが出る。

Can't vivify reference at tmp.pl line 16.

参考

qiita.com

developer.hatenastaff.com