Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

"A meditation on Hashes", or "Why i need more aspirin"

by EvanK (Chaplain)
on Jun 15, 2006 at 22:29 UTC ( [id://555651]=perlmeditation: print w/replies, xml ) Need Help??

I've been dealing with hashes and references a lot lately, and I wrote a test script to re-educate myself on the differences between hash keys existing, being defined, and containing data...I thought I'd post it cause it's interesting, albiet far from news.

So, I declare a hash, and work with four keys:

  • a, which contains string data
  • b, which contains an empty string
  • c, which contains an undef value
  • d, which is never even declared
and the code, of course:
#!/usr/bin/perl -w use strict; my %hash; $hash{'a'} = "alpha"; $hash{'b'} = ""; $hash{'c'} = undef; print "a='" . $hash{'a'} . "'\n"; print "b='" . $hash{'b'} . "'\n"; print "c='" . $hash{'c'} . "'\n"; print "d='" . $hash{'d'} . "'\n\n"; print "exists(a)='" . exists($hash{'a'}) . "'\n"; print "exists(b)='" . exists($hash{'b'}) . "'\n"; print "exists(c)='" . exists($hash{'c'}) . "'\n"; print "exists(d)='" . exists($hash{'d'}) . "'\n\n"; print "defined(a)='" . defined($hash{'a'}) . "'\n"; print "defined(b)='" . defined($hash{'b'}) . "'\n"; print "defined(c)='" . defined($hash{'c'}) . "'\n"; print "defined(d)='" . defined($hash{'d'}) . "'\n\n"; print "a is true\n" if($hash{'a'}); print "b is true\n" if($hash{'b'}); print "c is true\n" if($hash{'c'}); print "d is true\n" if($hash{'d'});
so, as we can see from the output of this, a key can be totally undeclared (not exist), can be declared but undefined (equals undef), OR be declared AND defined yet empty (equals an empty string), all in addition to actually having a populated value. and THEN we can look at which ones evaluate to being true :o

i know that this is one of those aspects of perl that scares away sooo many newcomers, and its a shame. because even though perl can confuse a 5+ year user like myself so easily, its complexity is exactly what makes it so versatile.

so while many here already understand everything said, perhaps a few newcomers will read this and walk away not so frightened by hashes and keys and values, oh my!

edit: rearranged the code to make a bit more sense, props to ikegami for pointing it out

__________
Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.
- Terry Pratchett

Replies are listed 'Best First'.
Re: "A meditation on Hashes", or "Why i need more aspirin"
by xdg (Monsignor) on Jun 15, 2006 at 23:51 UTC

    I think one of the potentially more confusing parts for newcomers is understanding how hash references autovivify. For example:

    use strict; use warnings; local $\ = "\n"; my %hash; print '$hash{a} ', ( $hash{a} ) ? "true" : "not true +"; print '$hash{a} ', ( defined $hash{a} ) ? "defined" : "not defi +ned"; print '$hash{a} ', ( exists $hash{a} ) ? "exists" : "doesn't +exist"; print ""; print '$hash{a}{b} ', ( $hash{a}{b} ) ? "true" : "not true +"; print '$hash{a}{b} ', ( defined $hash{a}{b} ) ? "defined" : "not defi +ned"; print '$hash{a}{b} ', ( exists $hash{a}{b} ) ? "exists" : "doesn't +exist"; print ""; print '$hash{a} ', ( $hash{a} ) ? "true" : "not true +"; print '$hash{a} ', ( defined $hash{a} ) ? "defined" : "not defi +ned"; print '$hash{a} ', ( exists $hash{a} ) ? "exists" : "doesn't +exist";

    Prints:

    $hash{a} not true $hash{a} not defined $hash{a} doesn't exist $hash{a}{b} not true $hash{a}{b} not defined $hash{a}{b} doesn't exist $hash{a} true $hash{a} defined $hash{a} exists

    Checking for $hash{a}{b} causes $hash{a} to suddenly contain the reference to an anonymous hash that is checked for the 'b'.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

      Checking for $hash{a}{b} causes $hash{a} to suddenly contain the reference to an anonymous hash that is checked for the 'b'.
      And the exception to this is when using threads::shared:
      Taking references to the elements of shared arrays and hashes does not autovivify the elements, and neither does slicing a shared array/hash over non-existent indices/keys autovivify the elements.
      Thus, the following results in a runtime error:
      use strict; use warnings; use threads; use threads::shared; my %hash :shared; print '$hash{a}{b} ', ( exists $hash{a}{b} ) ? "exists" : "doesn't ex +ist";

      Remember: There's always one more bug.
Re: "A meditation on Hashes", or "Why i need more aspirin"
by jwkrahn (Abbot) on Jun 16, 2006 at 02:11 UTC
    Don't forget that:
    $hash{'b'} = "";
    And:
    $hash{'b'} = "0";
    Do the same thing in your example.
Re: "A meditation on Hashes", or "Why i need more aspirin"
by fireartist (Chaplain) on Jun 16, 2006 at 12:54 UTC

    Maybe including the program output would be helpful, then point out which results you find surprising.

    The results of defined() on "" or undef apply to any perl variable, not just hashes, and a hash key existing after being assigned "" or undef shouldn't really be that surprising.

    After running this myself, the only outcome that I didn't expect, was that exists $hash{d} returns false after print $hash{d}. I had expected the print to autovivify the d key to contain undef.

      After running this myself, the only outcome that I didn't expect, was that exists $hash{d} returns false after print $hash{d}. I had expected the print to autovivify the d key to contain undef.

      The thing to remember is that it is not hash values that autovivify -- it is anonymous hash and array references that autovivify. For example, given an undef scalar, you can autovivify the anonymous hash just by coding as if the scalar contained a hash reference:

      use strict; use warnings; local $\="\n"; my $hr; print '$hr ', defined $hr ? 'defined' : 'undefined'; print '$hr->{a} ', exists $hr->{a} ? 'exists' : 'doesn\'t exist'; print '$hr ', defined $hr ? 'defined' : 'undefined'; print '$hr is ', $hr;

      Prints:

      $hr undefined $hr->{a} doesn't exist $hr defined $hr is HASH(0x3d51c0)

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: "A meditation on Hashes", or "Why i need more aspirin"
by TGI (Parson) on Jun 16, 2006 at 22:35 UTC

    It's also interesting to note how undefined and null string values are handled as hash keys.

    my %hash; my $a = undef; my $b = ''; $hash{$a} = 'foo'; print "$hash{$a}\n"; print "$hash{$b}\n"; <code> <p>Of course both these throw a warning. <code> C:\temp>hash.pl Use of uninitialized value in hash element at C:\temp\hash.pl line 8. Use of uninitialized value in hash element at C:\temp\hash.pl line 9. foo foo

    It turns out I didn't really understand autovivification as well as I thought I did. Thanks for the node.


    TGI says moo

Re: "A meditation on Hashes", or "Why i need more aspirin"
by nothingmuch (Priest) on Jun 20, 2006 at 10:00 UTC
    A lesser known fact is that you can delete array cells too.. The aspirin in this case is to think of arrays as arrays of pointers to scalars. If the pointer is nothing then the array cell doesn't exist. If the pointer leads to a scalar then you have a cell in which to store values. I think this is how arrays are implemented internally...
    #!/usr/bin/perl use strict; use warnings; no warnings 'uninitialized'; my @array = 1 .. 5; $\ = "\n"; sub check { print "array length: " . @array; use tt; [% FOR check IN ["exists", "defined", "true", "" ] %] for my $i ( 0 .. $#array ) { print "[% check %]([$i]) is " . [% check %]( $array[$i] ); } [% END %] no tt; print; } sub true { !!$_[0] } check; delete $array[$_] for 2 .. 3; # creates a hole check; undef $array[0]; # operates on the slot, not the array check; delete $array[-1]; # reduces length check; undef @array; # clears the array check; @array = 1 .. 3; check;
    This yields:
    -nuffin
    zz zZ Z Z #!perl

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://555651]
Approved by GrandFather
Front-paged by friedo
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (4)
As of 2024-04-19 16:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found