tags:

views:

142

answers:

3

Hi,

I think the title is self-explanatory. Many times I have small typos and I get unexpected results when trying to access undefined hash keys. I know I can add some defined check before each time I access a hash key, but I wonder if there's any cleaner way to warn against such cases....

Best, Dave

+6  A: 

Use Hash::Util:

use Hash::Util "lock_keys";
my %hash = (foo => 42, bar => 23);
lock_keys(%hash);
print $hash{foo};
print $hash{baz};
print $hash{bar};

output:

42
Attempt to access disallowed key 'baz' in a restricted hash at foo line 5.

There are other functions that allow specifying which keys to allow, not just defaulting to what's already there.

ysth
Thank you, this is nice, but I wonder if there's something that better suits my needs. I would like an error to be generated whenever I try to access an undefined key, unless the access is an assignment.My hash is dynamic - I add keys and get keys all the time - so using Hash::Util will make me lock and unlock continuously and I might as well check if the key is defined.
David B
@David B: then you probably want to use a tied hash, as Eric Strom suggests. Though I wonder what exactly you are doing with the values you get? Many operations should already be warning about undefined values, assuming you have warnings enabled.
ysth
+4  A: 

You can write a simple function for this:

sub get {
    my ($hash, $key) = @_;
    die "No such key: $key" unless exists $hash->{$key};
    return $hash->{$key};
}

my %hash = (...);    
my $val = get(\%hash, "mykey");
eugene y
+12  A: 

This is probably best done with a tied hash. Tied variables allow you to define the implementation of the low level operations of the variable. In this case, we want a special fetch method that dies when accessing non-existant keys:

use warnings;
use strict;

{package Safe::Hash;
        require Tie::Hash;
        our @ISA = 'Tie::StdHash';
        use Carp;

        sub FETCH { 
                exists $_[0]{$_[1]} or croak "no key $_[1]";
                $_[0]{$_[1]}
        }
}

tie my %safe => 'Safe::Hash';

$safe{a} = 5;  # ok

print $safe{a}, "\n";  # ok

$safe{b} = 10; # ok 

print $safe{bb}, "\n";  # dies

In the implementation of Safe::Hash above, I first load Tie::Hash which provides Tie::StdHash. Setting @ISA to Tie::StdHash provides our new package with tie methods that behave the same way as normal hashes. Each of the tie methods are outlined on http://perldoc.perl.org/perltie.html

In this case the only method to override is FETCH which is passed a reference to the hidden tied object (a hashref in this case), and the key to use. It checks if the slot exists, and either returns it or throws an error

Eric Strom
very cool! could you please explain the FETCH part? is this a reserved word?
David B
Tied variables provide a set of methods for you to override that allow you to specify their behavior. See perltie for all the details: http://perldoc.perl.org/perltie.html
friedo