Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

help with references

by dw_perlmonks (Novice)
on May 04, 2013 at 19:07 UTC ( #1032071=perlquestion: print w/replies, xml ) Need Help??
dw_perlmonks has asked for the wisdom of the Perl Monks concerning the following question:

Hi there esteemed Monks,
I have a very noob-ish question here regarding refs, though still confounding to me at the very least...
In the code example below, i'm trying to create a hash of arrays:
#!/usr/bin/perl use strict; use warnings; use 5.010; use Data::Dumper; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Quotekeys = 0; my @a1 = ( 'a1', 1, 1, 1 ); my @a2 = ( 'a2', 2, 2, 2 ); my $a1_ref = \@a1; my $a2_ref = \@a2; my @a = ( $a1_ref, $a2_ref ); my %h = (); for my $i ( 1 .. 2 ) { $h{"$i"} = \@a; } say Dumper \%h;

The Dumper output is
{ '1' => [ [ 'a1', 1, 1, 1 ], [ 'a2', 2, 2, 2 ] ], '2' => $VAR1->{'1'} }

The question here is:
why is $h{'2'} a reference to $h{'1'}? I'm trying to create a hash %h with identical key-values made of the @a array-of-arrays. I want each key-value of the hash to have it's own AoA based on @a, but i'm getting references to $h{'1'} instead. What am i doing wrong??
The Dumper output i'm trying to achieve is:
{ '1' => [ [ 'a1', 1, 1, 1 ], [ 'a2', 2, 2, 2 ] ], '2' => [ [ 'a1', 1, 1, 1 ], [ 'a2', 2, 2, 2 ] ] }
Any help appreciated. thanks in advance!

Replies are listed 'Best First'.
Re: help with references
by moritz (Cardinal) on May 04, 2013 at 19:18 UTC

    Both times that the loop is run, \@a returns a reference to the same array, because it is the same array that's on the right of the \ character.

    To avoid that, you can use $h{$i} = [@a] instead, which copies the array and returns a reference to the copy.

      Thanks for the reply, moritz! using your method i'm no longer getting a ref of @a, but now i get refs of the individual arrays instead. it still won't work because if i change the AoA in $h{"1"}, the AoA in $h{"2"} will also be modified.
      { '1' => [ [ 'a1', 1, 1, 1 ], [ 'a2', 2, 2, 2 ] ], '2' => [ $VAR1->{'1'}[0], $VAR1->{'1'}[1] ] }
Re: help with references
by tobyink (Abbot) on May 04, 2013 at 19:22 UTC

    $h{2} isn't a reference to $h{1}. $h{2} is a reference to the same array that $h{1} refers to.

    I think you probably want:

    for my $i ( 1 .. 2 ) { my @new = @a; # copy of @a $h{"$i"} = \@new; # reference the copy }

    Or (more concise way of saying the same thing)...

    for my $i ( 1 .. 2 ) { $h{"$i"} = [@a]; }
    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      tobyink, thanks for replying. But what I was expecting is different. I've updated my post with more info.
Re: help with references
by poj (Monsignor) on May 04, 2013 at 19:49 UTC
    Try adding
    $Data::Dumper::Deepcopy = 1;
Re: help with references
by 2teez (Vicar) on May 04, 2013 at 23:31 UTC

    "The Dumper output i'm trying to achieve is..":
    Has rightly pointed out by moritz, use Storable's freeze and thaw or dclone like so:

    ... use Storable qw(thaw freeze); ... my $frozen = freeze \@a; for my $i ( 1 .. 2 ) { $h{"$i"} = thaw $frozen; } ...
    OR using dclone, in a single step
    ... use Storable qw(dclone); ... for my $i ( 1 .. 2 ) { $h{"$i"} = dclone \@a; } ...
    The Dumper output will be what you are looking for.
    Hope this helps

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me
Re: help with references
by sundialsvc4 (Abbot) on May 06, 2013 at 13:30 UTC

    It might help to “clear your mind and” consider this:   there is no such thing as a hash of arrays.   Or an array of hashes, or an array of arrays, or anything else.   All of Perl’s multi-element data structures (lists, arrays, hashes) are one-dimensional.

    The “secret sauce” is, of course, a reference, which is a single thing.   Any scalar-variable, or any bucket in a list, array, or hash, can therefore contain a “reference” just as easily as it could contain a “number” or a “string” or any other single-thing.   But a reference can refer to ... anything at all.   It can refer to a scalar, or any of the multi-element data structures, or anything else.   Furthermore, all of the things that it can refer-to have a “reference count,” which tracks how many references to them currently exist, so that nothing will be garbage-collected while any (non-weakened) references to it remain.   You can create very complex data-structures with impunity and never worry about a C/C++ style “segfault.”

    But you do have to know what is going on.   Perl is very much a “DWIM = Do What I Mean” language.   It has several syntactic shortcuts ... which can indeed be very deceptive if you do not fully understand how Perl actually interprets them.   Unlike many programming tools, there isn’t a static indication in the source-code that a reference is being used at any particular point:   many determinations are made by the Perl interpreter at run-time, as it tries to DWYM.   So, you look at the source-code (which is syntactically valid), and don’t see the problem.

    In your example, you created several references to ... the same chunk of data ... at several distinct places in your array.   To Perl, this has one thing being located at several places at one time ... that is to say, there are several references to the same data, each located in different spots.   Some of the various solutions that you’ve been shown involve making “deep copies,” that is to say, truly independent duplicates, of this data ... which might well be what you, in your mind’s eye, were intending to do all along.

    Notice how Dumper is “references aware.”   It recognizes that the second element of the array is a reference to the identical block of data as the first, and it is designed to show you that.   In your code, $VAR1 (or whatever its name is ...) is a hash with two tags, and both tags contain a reference to the same piece of data.   Literally, to the same memory bytes, which therefore currently have a “reference count” at least equal to 2.   In Fortran parlance, they are EQUIVALENCEd.

Re: help with references
by dw_perlmonks (Novice) on May 09, 2013 at 22:53 UTC
    Thank you all for your input! Not only did i find the correct solution, but also got much needed clarification on the way structures work in Perl. So the solution i used, as described by @moritz, was to use the Clone mode. And that solved it!

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1032071]
Approved by ww
Front-paged by 2teez
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (7)
As of 2018-11-21 14:35 GMT
Find Nodes?
    Voting Booth?
    My code is most likely broken because:

    Results (239 votes). Check out past polls.