"A meditation on Hashes", or "Why i need more aspirin"
EvanK
created: 2006-06-15 18:29:11
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:

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 "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 "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 "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!

__________
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

Re: "A meditation on Hashes", or "Why i need more aspirin"
xdg
created: 2006-06-15 19:51:33

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 defined";
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 defined";
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 defined";
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.

Re^2: "A meditation on Hashes", or "Why i need more aspirin"
created: 2006-06-15 20:54:09
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 [mod://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 exist";

Remember: There's always one more bug.
Re: "A meditation on Hashes", or "Why i need more aspirin"
created: 2006-06-15 22:11:17
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"
created: 2006-06-16 08:54:30

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.

Re^2: "A meditation on Hashes", or "Why i need more aspirin"
xdg
created: 2006-06-16 09:20:42
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 [http://creativecommons.org/licenses/publicdomain|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"
TGI
created: 2006-06-16 18:35:06

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";


Of course both these throw a warning. 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 [http://www.sysarch.com/Perl/autoviv.txt|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"
created: 2006-06-20 06:00:17
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:
array length: 5
exists([0]) is 1
exists([1]) is 1
exists([2]) is 1
exists([3]) is 1
exists([4]) is 1
defined([0]) is 1
defined([1]) is 1
defined([2]) is 1
defined([3]) is 1
defined([4]) is 1
true([0]) is 1
true([1]) is 1
true([2]) is 1
true([3]) is 1
true([4]) is 1
([0]) is 1
([1]) is 2
([2]) is 3
([3]) is 4
([4]) is 5

array length: 5
exists([0]) is 1
exists([1]) is 1
exists([2]) is 
exists([3]) is 
exists([4]) is 1
defined([0]) is 1
defined([1]) is 1
defined([2]) is 
defined([3]) is 
defined([4]) is 1
true([0]) is 1
true([1]) is 1
true([2]) is 
true([3]) is 
true([4]) is 1
([0]) is 1
([1]) is 2
([2]) is 
([3]) is 
([4]) is 5

array length: 5
exists([0]) is 1
exists([1]) is 1
exists([2]) is 1
exists([3]) is 1
exists([4]) is 1
defined([0]) is 
defined([1]) is 1
defined([2]) is 
defined([3]) is 
defined([4]) is 1
true([0]) is 
true([1]) is 1
true([2]) is 
true([3]) is 
true([4]) is 1
([0]) is 
([1]) is 2
([2]) is 
([3]) is 
([4]) is 5

array length: 4
exists([0]) is 1
exists([1]) is 1
exists([2]) is 1
exists([3]) is 1
defined([0]) is 
defined([1]) is 1
defined([2]) is 
defined([3]) is 
true([0]) is 
true([1]) is 1
true([2]) is 
true([3]) is 
([0]) is 
([1]) is 2
([2]) is 
([3]) is 

array length: 0

array length: 3
exists([0]) is 1
exists([1]) is 1
exists([2]) is 1
defined([0]) is 1
defined([1]) is 1
defined([2]) is 1
true([0]) is 1
true([1]) is 1
true([2]) is 1
([0]) is 1
([1]) is 2
([2]) is 3
-nuffin
zz zZ Z Z #!perl

perlmonks.org content © perlmonks.org and EvanK, fireartist, jdhedden, jwkrahn, nothingmuch, TGI, xdg

prlmnks.org © 2006 edmund von der burg (eccles & toad)

v 0.03