Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

hash ref mind blow

by Anonymous Monk
on Sep 24, 2008 at 16:03 UTC ( [id://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 (Patriarch) 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
Domain Nodelet?
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?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (12)
As of 2024-04-23 08:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found