Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

HoH Weirdness

by Davious (Sexton)
on Jun 08, 2001 at 22:08 UTC ( [id://86999]=perlquestion: print w/replies, xml ) Need Help??

Davious has asked for the wisdom of the Perl Monks concerning the following question:

I've been working with a program that uses Hashes of Hashes and have run into some odd results. My understanding was that a hash key could only have one value, either a string or a reference. So why is it that the following code prints "Red Apple", shouldn'y the second line overwrite the value of $hash{'food'} with a reference?
$hash{'food'} = 'Apple'; $hash{'food'}{'color'} = 'Red'; print "$hash{'food'}{'color'} $hash{'food'}\n";
and if that works, and the key is remembering both the string and the reference, then why does this print " Apple"?
$hash{'food'}{'color'} = 'Red'; $hash{'food'} = 'Apple';
I'm assuming it's because 'Apple' overwrites the reference, but wait, it gets weirder. In the following example, the values of one hash seemingly effect the values of another (completely unrelated) hash.
$hash1{'food'}{'color'} = 'Red'; $hash1{'food'} = 'Apple'; $hash2{'food'}{'color'} = 'Red'; $hash2{'food'} = 'Apple'; print "hash1: $hash1{'food'}{'color'} $hash1{'food'}\n"; print "hash2: $hash2{'food'}{'color'} $hash2{'food'}\n";
These both print " Apple" but if you swap the first two lines like so:
$hash1{'food'} = 'Apple'; $hash1{'food'}{'color'} = 'Red'; $hash2{'food'}{'color'} = 'Red'; $hash2{'food'} = 'Apple'; print "hash1: $hash1{'food'}{'color'} $hash1{'food'}\n"; print "hash2: $hash2{'food'}{'color'} $hash2{'food'}\n";
Then they both print "Red Apple"! How could %hash1 effect %hash2?

I tested this on both Win2000 Perl 5.6.0 and FreeBSD Perl 5.005.

Replies are listed 'Best First'.
Re: HoH Weirdness
by danger (Priest) on Jun 08, 2001 at 22:53 UTC

    You are actually creating a symbolic reference, creating a new hash named "Apple" in the case of:

    $hash{'food'} = 'Apple'; $hash{'food'}{'color'} = 'Red'; print "$hash{food}{color}\n"; print "$Apple{color}\n";

    This might be more apparent if you look at the dereference in this manner:

    $hash{'food'} = 'Apple'; ${$hash{'food'}}{'color'} = 'Red'; print "$hash{food}{color}\n"; print "$Apple{color}\n";

    You can see that you are using a string as a reference. However, in the following situation:

    $hash{food}{color} = 'Red'; $hash{food} = 'Apple';

    Because $hash{food} is undefined, Perl autovivifies the hash reference and stores it as the value for the key 'food'. The next line simply overwrites that value with the string 'Apple'. The key is knowing that Perl only autovivifies when you dereference an undefined value as a reference. Then we get to your final example:

    $hash1{'food'} = 'Apple'; $hash1{'food'}{'color'} = 'Red'; $hash2{'food'}{'color'} = 'Red'; $hash2{'food'} = 'Apple'; print "hash1: $hash1{'food'}{'color'} $hash1{'food'}\n"; print "hash2: $hash2{'food'}{'color'} $hash2{'food'}\n";

    And one hash is mysteriously affecting another --- but really, your the second statement result in a symbolic reference to a hash %Apple with a key 'color' and value 'Red'. The first hash2 assignment does autovivify a real hash reference, but that is overwritten in the second hash2 assignment. When you print out hash2, the first value is actually dereferencing the symbolic %Apple hash, just like before. This kind of confusion is a good reason to *not* use symbolic references and to always use 'strict'.

Re: HoH Weirdness
by lestrrat (Deacon) on Jun 08, 2001 at 22:21 UTC

    Somehow I think you're not using strict.

    On my machine $hash{ 'key1' }{ 'key2' } gives me a compilation time error with strict

    Perhaps you meant $hash{ 'key1' }->{ 'key2' } ?

    Either way, do this to verify if your hashes are working the way you want them to...

    use Data::Dumper; print Dumper( \%hash );

    This is quick and dirty, but at least you get to do a sanity check as to what's in the hash...

    ----

    update: added explanation in a separate node upon request :)

    update: a few people have gotten me. for those of you reading my posts in this thread, please make sure to read these two posts, cause I screwed up. :(

      Perhaps you meant $hash{ 'key1' }->{ 'key2' } ?

      Care to continue that thought with an explaination of how

      $hash{ 'key1' }->{ 'key2' }
      is different in meaning from
      $hash{ 'key1' }{ 'key2' }
      ?

        As everybody else says,

        $hash{ 'key1' }{ 'key2' }

        takes the contents of $hash{ 'key1' } to be a symbolic reference, and then tries to get the value of that corresponds to 'key2' against a hash with the name = $hash{ 'key1' }

        So

        $hash{ 'key1' } = 'foo'; $hash{ 'key1' }{ 'key2' } = 'bar'; # (2) # (2) means find %(current pkg)::foo and do $(current pkg)::foo{ 'key2' } = 'bar';

        Still a hash of hashes, but I find this rather tricky to use. You always have to be aware of the fact that $hash{ 'key1' }{ 'key2' } is being evaluated via symbols

        However, $hash{ 'key1' }->{ 'key2' } would be used like this:

        my %hash; $hash{ 'key1' } = {}; ## ref to hash $hash{ 'key1' }->{ 'key2' } = 'foo'; ## above equivalent to my %hash = ( key1 => { key2 => 'foo' } );

        And, of course, I wrote up to here and realized that it doesn't really fit to the original problem! sigh.

        I probably should have pointed to

        $hash{ 'key1' } = 'foo'; $hash{ 'key1', 'key2' } = 'bar';

        But I really dislike that notation. ( and the way things gets stored when you do it that way )

        As for the original problem, I would have done:

        my %hash = ( food => { name => 'Apple', color => 'Red' } ); my $food = $hash{ 'food' }; print "$food->{ 'color' } $food->{ 'name' }\n";

        Is this explanation better ? :-)

Re: HoH Weirdness
by dragonchild (Archbishop) on Jun 08, 2001 at 22:39 UTC
    The answer to the first section of code is rather simple. The string 'Apple' is being used as a reference. If you compile it under "use strict 'refs'" (or just "use strict"), you'll see what I'm talking about.

    Now, as to how using a string as a reference works ... I'm a little fuzzier on that. But, I do like using it, in very controlled doses. :)

    Now, as to the second question ... I did a little further playing around and found that so long as the assignment of Apple never happened before the assignment of Red, for that given hash, neither hash correctly printed "Red Apple".

    <speculation>
    I'm going to guess that the moment the interpreter sees that the string 'Apple' can be made into a reference, it marks 'Apple' as a valid reference for everyone. Why it affects the hash seemingly defined before it? Because, the interpreter, in its first pass, will find all the possible references and remember those. Then, in the second pass, it will use those references it knows about.
    </speculation>

    Nifty theoretical problem!

      I'm going to guess that the moment the interpreter sees that the string 'Apple' can be made into a reference, it marks 'Apple' as a valid reference for everyone. Why it affects the hash seemingly defined before it? Because, the interpreter, in its first pass, will find all the possible references and remember those. Then, in the second pass, it will use those references it knows about.
      No, that's too much voodoo. It's simpler than that. The first hash assignment puts values in %main::Apple, using a symref (use strict would have caught that.) And the second set of assignments causes it to reference %main::Apple as well, the very same hash.

      No magic needed to explain that.

      -- Randal L. Schwartz, Perl hacker

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://86999]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (5)
As of 2024-04-23 06:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found