perlautovivification

How to avoid hash autovivification?


Let's say I have a Perl script that does:

my $hash = {};
$hash->{'a'} = {aa => 'b'};
$hash->{'b'} = undef;

for (qw(a b c)) {
    if(defined $hash->{$_}->{aa})
    {
        say "defined $_";
    }
    else
    {
        say "undef $_";
    }
}
print Dumper $hash;

But, my output autocreates 'c', which I don't want.

defined a
undef b
undef c
$VAR1 = {
      'c' => {},
      'a' => {
               'aa' => 'b'
             },
      'b' => {}
    };

Also my distribution does not allow me to disable autovivification. Is there a way to make a subroutine that checks each level?


Solution

  • Here's a simple function that checks each level for existence and stops without autovivifying intermediate levels. The prototype isn't necessary; it makes it act (sort of) like the built-in defined. This version only works for nested hashes (except blessed hashes and overloaded objects that look like hashes).

    sub noVivDefined(+@) {
        my ($x, @keys) = @_;
        foreach my $k (@keys) {
            return unless ref $x eq 'HASH';
            return unless exists $x->{$k};
            $x = $x->{$k};
        }
        return defined $x;
    }
    
    my %h = (
        a => { b => 1 },
        b => { b => 0 },
        c => { b => undef },
        d => { c => 1 },
    );
    
    
    say noVivDefined %h, qw(a b);   # prints '1'
    say noVivDefined %h, qw(a b c); # prints ''
    say noVivDefined %h, qw(x y z); # prints ''
    say noVivDefined %h, qw(b b);   # prints '1'
    say noVivDefined %h, qw(c b);   # prints ''
    say noVivDefined %h, qw(d b);   # prints ''
    say noVivDefined \%h, qw(a b);  # prints '1'