Eric Fossum
Eric Fossum

Reputation: 2461

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?

Upvotes: 3

Views: 322

Answers (3)

Dondi Michael Stroma
Dondi Michael Stroma

Reputation: 4800

What is wrong with:

if (exists $hash->{$_} and defined $hash->{$_}->{aa})

Upvotes: 4

Michael Carman
Michael Carman

Reputation: 30831

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'

Upvotes: 1

mob
mob

Reputation: 118605

Call no autovivification in the desired scope:

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

If the autovivification pragma is not available, the usual idiom is to test for existence of the top level hash before you run a check at a deeper level:

if ($hash && $hash->{$_} && defined($hash->{$_}->{aa}) {
   ...
}

Upvotes: 12

Related Questions