Re: Hashes of Hashes



Jason Malburg wrote:
I'm working on a complex data structure with hashes of hashes and
hashes of arrays and I noticed a weird behavior I was hoping someone
could explain. Here's a sample:

$Data{US17}{1}{GROUP} = "STRING";
$Data{US17}{1}{GROUP}{.001} = 5;
$Data{US17}{1}{GROUP}{.002} = 6;
$Data{US17}{1}{GROUP}{.003} = 7;
print "group = $Data{US17}{1}{GROUP}\n";
for my $time (keys %{$Data{US17}{1}{GROUP}}){
print "time = $time $Data{US17}{1}{GROUP}{$time}\n";
}

The output is:
group = STRING
time = 0.002 6
time = 0.003 7
time = 0.001 5

If I swap the order of the declarations, then all of the keys for the
hash under 'GROUP' are wiped out. For example:
$Data{US17}{1}{GROUP}{.001} = 5;
$Data{US17}{1}{GROUP}{.002} = 6;
$Data{US17}{1}{GROUP}{.003} = 7;
$Data{US17}{1}{GROUP} = "STRING";

only produces:
group = STRING

Is this expected behavior? Am I getting lucky in the first instance
with bad syntax that just happens to do what I want?

Yes, that's exactly correct. What you are doing is using "symbolic
references". The first line:

$Data{US17}{1}{GROUP} = "STRING";

assigns the string "STRING" to be the value of the key 'GROUP' in the
hash that is referenced by $Data{US17}{1}. Presuming this hash has no
other key/value pairs already created, the following is an identical
assignment:
%{$Data{US17}{1}} = ("GROUP" => "STRING");

Your next three statements, however, make an assumption that does not
correlate to this first one:
$Data{US17}{1}{GROUP}{.001} = 5;
$Data{US17}{1}{GROUP}{.002} = 6;
$Data{US17}{1}{GROUP}{.003} = 7;

These are now assigning the numbers 5, 6, and 7, to be values of the
keys ".001", ".002", and ".003" (respectively) of the hash that is
referenced by $Data{US17}{1}{GROUP}.

Do you see the conflict? You are making an assmption that
$Data{US17}{1}{GROUP} is a reference to a hash. But one statement
earlier, you said that $Data{US17}{1}{GROUP} is a string -
specifically, "STRING". So you're trying to say:
%{$Data{US17}{1}{GROUP}} = ( .001 => 5, .002 => 6, .003 => 7);
But because $Data{US17}{1}{GROUP} is a string, this is the same thing
as:
%{STRING} = (.001 => 5, .002 => 6, .003 => 7);

That is, Perl created a completely new variable, %STRING, and assigned
your hash to that. This is known as a "symbolic reference" - using the
value of one variable as the name of another variable. If you print
out, for example, $STRING{.001} you will see the value 5.

When you did it the other way around:
$Data{US17}{1}{GROUP}{.001} = 5;
$Data{US17}{1}{GROUP}{.002} = 6;
$Data{US17}{1}{GROUP}{.003} = 7;
these three statements make $Data{US17}{1}{GROUP} into a hash reference
automatically - through a process known as "autovivification". If an
undefined value (such as a previously non-existent value of a hash) is
used as a hash reference, a real hash reference springs into existence.
But then immediately after implicitly creating this hash reference,
you do:
$Data{US17}{1}{GROUP} = "STRING";
Which changes the value of $Data{US17}{1}{GROUP}. It is no longer that
hash reference you created. Now it's a simple string. Therefore, the
hash that was previously referenced here simply goes away. And you
lose the three key/value pairs you'd added to it.

Symbolic references are generally a very bad idea (see: perldoc -q
"variable name"). If you put the line:
use strict;
at the top of your program, Perl will prevent you from using a symbolic
reference. This is a good thing.

As for how to solve your original problem while avoiding hash
references, you'll have to rethink your data structure a bit. You
can't have $Data{US17}{1}{GROUP} be both a string and a hash reference.
I would suggest leaving it a hash reference, and possibly adding
another level of indirection:
$Data{US17}{1}{GROUP}{string_val} = "STRING";
$Data{US17}{1}{GROUP}{num_vals}{.001} = 5;
$Data{US17}{1}{GROUP}{num_vals}{.002} = 6;
$Data{US17}{1}{GROUP}{num_vals}{.003} = 7;

Hope this helps,
Paul Lalli

.



Relevant Pages

  • Re: Reference question
    ... Paul Lalli wrote: ... >>> If you want to get a reference without the extraneous temporary ... >> values are refs to another hash, whose keys are strings and values are ...
    (perl.beginners)
  • Re: Scalar Reference Explanation?
    ... The key to a hash is a string. ... Perl therefore stringifies it and uses the string value as the value key. ... But the stringified version of a reference is no longer a reference. ... print "Ref is $key\n"; ...
    (comp.lang.perl.misc)
  • Re: hash two keys to one index
    ... What goes into the map are pairs of (reference to key, ... When I insert an object into the hash table, I pass in ... void insert(Object obj, int hash) throws HashTableFull ... int probe = 0; ...
    (comp.lang.java.programmer)
  • Re: Net::SFTP::Attributes
    ... when I ask a simple question on the correct usage of a module I would ... If $subref is specified, for each entry in the directory, $subref will ... be called and given a reference to a hash with three keys: ...
    (comp.lang.perl.modules)
  • Re: hash two keys to one index
    ... What goes into the map are pairs of (reference to key, ... When I insert an object into the hash table, I pass in ... void insert(Object obj, int hash) throws HashTableFull ... int probe = 0; ...
    (comp.lang.java.programmer)

Quantcast