Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

hash ref mind blow

by Anonymous Monk
on Sep 24, 2008 at 16:03 UTC ( #713457=perlquestion: print w/replies, xml ) Need Help??

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

Please, help!
use strict; my %hash = (); $hash{a}{drinks}=1; $hash{b}{drinks}=2; print keys %hash; ## printed a b print map { $hash{$_}{drinks} } keys %hash; ## printed 1 2 my $p = \%hash; my %copy = %{ $p }; print keys %copy; ## printed a b ## - fine, %hash has been copied to %copy $copy{c}=3; print keys %hash; ## printed a b ## - ok, %copy is something else ## and %hash won't change when %copy changes. print "REF_HASH=", \%hash, ", REF_copy=", \%copy; ## REF_HASH=HASH(0xcfbf0c), REF_COPY=HASH(0xd05168) ## - yup, they're different indeed. $copy{a}{drinks}=4; print map { $hash{$_}{drinks} } keys %hash; ## printed 4 2 !?!?!?! ## I don't get it! ## Why did %hash change here but didn't before, ## when I added the new key 'c'?
Thanks! -rod

Replies are listed 'Best First'.
Re: hash ref mind blow
by Corion (Pope) on Sep 24, 2008 at 16:13 UTC

    %copy is just a shallow copy. That means, it shares the references to all { drinks => ... } hashes with %hash. If you want to do a deep copy that copies all values and all referenced data structures, have a look at Storable::dclone:

    my %copy = dclone(\%hash);

    should be all you need.

      Got it. Thanks!! And I thought I knew perl... Never heard of shallow copies. Anybody knows which perldoc has some material on this? perlref doesn't seem to mention them at all. -rod

        You might think of

        $hash{a}{drinks}=1; $hash{b}{drinks}=2;

        as a single hash, but that's not the case. There are three:

        • %hash
        • %{ $hash{a} }
        • %{ $hash{b} }
        +-------+ +----------+ +-----------+ +-------------+ | %hash | +-->| Anon Ref | +->| Anon hash | +->| Anon scalar | +-------+ | +----------+ | +-----------+ | +-------------+ | a ----+ | addr ----+ | drinks ----+ | 1 | | b -----+ +----------+ +-----------+ +-------------+ +-------+ | | +----------+ +-----------+ +-------------+ +->| Anon Ref | +->| Anon hash | +->| Anon scalar | +----------+ | +-----------+ | +-------------+ | addr ----+ | drinks ----+ | 2 | +----------+ +-----------+ +-------------+

        When you do %copy = %hash (which you wrote as %copy = %{ \%hash }), you are copying the keys and values of %hash. So what are the keys and values of %hash?

        >perl -e"$h{a}{d}=1; $h{b}{d}=2; print qq{$_: $h{$_}\n} for keys %h;" a: HASH(0x22534c) b: HASH(0x2253a0)

        That's all that's copied, nothing more. It will not make a copy of the referenced value and make a new reference to it. It simply copies the references. You end up with

        +-------+ +----------+ +-----------+ +-------------+ | %hash | +-->| Anon Ref | +----->| Anon hash | +->| Anon scalar | +-------+ | +----------+ | +-----------+ | +-------------+ | a ----+ | addr -----+ | drinks ----+ | 1 | | b -----+ +----------+ | +-----------+ +-------------+ +-------+ | | | +----------+ | +-----------+ +-------------+ +->| Anon Ref | | +-->| Anon hash | +->| Anon scalar | +----------+ | | +-----------+ | +-------------+ | addr --------+ | drinks ----+ | 2 | +----------+ | | +-----------+ +-------------+ | | +-------+ +NEW-------+ | | | %copy | +-->| Anon Ref | | | +-------+ | +----------+ | | | a ----+ | addr -----+ | | b -----+ +----------+ | +-------+ | | | +NEW-------+ | +->| Anon Ref | | +----------+ | | addr --------+ +----------+

        What you want is to do is to copy referenced values as well, and to do recursively, so that you end up with the following:

        +-------+ +----------+ +-----------+ +-------------+ | %hash | +-->| Anon Ref | +->| Anon hash | +->| Anon scalar | +-------+ | +----------+ | +-----------+ | +-------------+ | a ----+ | addr ----+ | drinks ----+ | 1 | | b -----+ +----------+ +-----------+ +-------------+ +-------+ | | +----------+ +-----------+ +-------------+ +->| Anon Ref | +->| Anon hash | +->| Anon scalar | +----------+ | +-----------+ | +-------------+ | addr ----+ | drinks ----+ | 2 | +----------+ +-----------+ +-------------+ +-------+ +NEW-------+ +NEW--------+ +NEW----------+ | %copy | +-->| Anon Ref | +->| Anon hash | +->| Anon scalar | +-------+ | +----------+ | +-----------+ | +-------------+ | a ----+ | addr ----+ | drinks ----+ | 1 | | b -----+ +----------+ +-----------+ +-------------+ +-------+ | | +NEW ------+ +NEW--------+ +NEW----------+ +->| Anon Ref | +->| Anon hash | +->| Anon scalar | +----------+ | +-----------+ | +-------------+ | addr ----+ | drinks ----+ | 2 | +----------+ +-----------+ +-------------+

        That's called a "deep copy" because it copies every level of the data structure. In contrast, a simple assignment is called a "shallow copy" because it only copies the immediate value (scalar/hash/array/...).

Re: hash ref mind blow
by duckyd (Hermit) on Sep 24, 2008 at 16:27 UTC
    The following might help to make things a little more clear. Hash has 2 values, which are both hash references (to a anonymous hashes { drinks => 1 } and { drinks => 2 }). When you do the shallow copy, the hash references are copied, but they still refer to the same hashes in both %hash and %copy:
    use strict; my %hash = (); $hash{a}{drinks}=1; $hash{b}{drinks}=2; my $p = \%hash; my %copy = %{ $p }; print "ref hash: ", \%hash, "\nref copy: ", \%copy, "\n"; print "values hash: ",join(", ", values %hash), "\nvalues copy: ",join +(", ", values %copy), "\n";
    Prints:
    copy: ab hash: ab ref hash: HASH(0x82ef7d8) ref copy: HASH(0x82ef928) values hash: HASH(0x82e0308), HASH(0x82ef788) values copy: HASH(0x82e0308), HASH(0x82ef788)
    Note that the values are identical, and thus the values of %hash and %copy refer to the same hash references.
      Yup, perfectly clear! Thank you! But I've just realized that I don't how to do one thing... how can I make %hash and %copy be in the same mem location? So that when I add a new key to %copy, %hash also changes. I'm just lazy about working with a ref directly as in
      $$p{c}=3;
      or
      $p->{c}=3;
      I just want
      $copy{c}=3;
      To change the value of %hash.
      -rod

        For global variables, just do a glob assignment:

        *copy = \%hash;

        then, %copy and %hash are the same data structure. Otherwise, that is, for lexical variables, use Data::Alias.

        Just so you know, Data::Dumper is part of every perl
        #!/usr/bin/perl -- use strict; my %hash = (); $hash{a}{drinks}=1; $hash{b}{drinks}=2; my $p = \%hash; my %copy = %{ $p }; use Data::Dumper; print Dumper( \%hash, \%copy, $p ); __END__ $VAR1 = { 'a' => { 'drinks' => 1 }, 'b' => { 'drinks' => 2 } }; $VAR2 = { 'a' => $VAR1->{'a'}, 'b' => $VAR1->{'b'} }; $VAR3 = $VAR1;
Re: hash ref mind blow
by NetWallah (Canon) on Sep 24, 2008 at 16:16 UTC
    You did a "Shallow copy" of the hash - merely copied it's (first level) reference.

    What what you are trying, you need to do a "Deep copy" - plenty of references to that in this monastery, and in CPAN modules.

    Update: Corion beat me to the explanation and CPAN references.

         Have you been high today? I see the nuns are gay! My brother yelled to me...I love you inside Ed - Benny Lava, by Buffalax

Re: hash ref mind blow
by merlyn (Sage) on Sep 25, 2008 at 02:56 UTC

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2021-05-11 08:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Perl 7 will be out ...





    Results (114 votes). Check out past polls.

    Notices?