in reply to Re^3: Strange hash array behavior
in thread Strange hash array behavior

Okay, cool that worked. I guess I just don't really conceptually get why the first iteration works but after that I get a shallow copy.

I think the reason why I am confused is because this works and it seems to be the same concept (to me):

my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" }, { "shape" => "oval", "food" => "grape" }, ]; my $test = $data1; my $test2 = $test; my $test3 = $test2; print Dumper($test3);
Update: Or an even better example of why I am confused:
my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" }, { "shape" => "oval", "food" => "grape" }, ]; foreach (0..5) { my $copy = $data1; print Dumper($copy); }
Update: And yet an even better example... why the heck does this work and not the previous examples?
use Data::Dumper; my $data1 = [ { "shape" => "round", "food" => "apple" }, { "shape" => "square", "food" => "pear" }, { "shape" => "oval", "food" => "grape" }, ]; my $data2 = [ { "big" => "cow", "small" => "bunny" }, { "big" => "horse", "small" => "mouse" }, ]; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; print Dumper($d); }
Last Update!: Using the same data above, this does not work:
my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,$d); } print Dumper(\@array);
But this does, so I guess I just don't understand why it (the above) works on the first iteration but not any after. Is that really behaving like it is suppose to?
my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,clone($d)); } print Dumper(\@array);
Thanks again for the fix!

Replies are listed 'Best First'.
Re^5: Strange hash array behavior
by ikegami (Patriarch) on Jan 29, 2009 at 15:26 UTC

    my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,$d); } print Dumper(\@array);

    The underlying problem is that you think $data2 contains a hash or array (depending on the snippet), but it doesn't. It contains a reference to a hash an an array.

    When you assign $data2 to something, you're copying the reference, that's it. All your questions boil down to "Why doesn't copy the referenced hash or array?", and the answer is always "Because you didn't ask it to."

Re^5: Strange hash array behavior
by ig (Vicar) on Jan 29, 2009 at 05:53 UTC
    I just don't really conceptually get why the first iteration works but after that I get a shallow copy.

    You never get a copy of the array referenced by $data2, not even the first time through the loop. Every time through the loop you set $r->{sizes} to be a reference to the same original anonymous array. If you want a new array you have to create it. util has shown you several ways you can make a copy of the array referenced by $data2. Having created a copy of the original array, you can then set $r->{sizes} to be a reference to the copy.

      You never get a copy of the array referenced by $data2, not even the first time through the loop.
      But, it seems to work on the first iteration. util's fix solved the problem and that is the route I am going to go. This is purely academic at this point. So, my last example above (using the same data)...
      my @array; foreach (0..2) { my $d = $data1->[$_]; $d->{sizes} = $data2; push(@array,$d); } print Dumper(\@array); $VAR1 = [ { 'food' => 'apple', 'shape' => 'round', 'sizes' => [ { 'small' => 'bunny', 'big' => 'cow' }, { 'small' => 'mouse', 'big' => 'horse' } ] }, { 'food' => 'pear', 'shape' => 'square', 'sizes' => $VAR1->[0]{'sizes'} }, { 'food' => 'grape', 'shape' => 'oval', 'sizes' => $VAR1->[0]{'sizes'} } ];
      But using util's solution, clone solves the problem:
      push(@array,clone($d));
      So, why does it "work" on the first pass (without using clone) and then from there I just get a shallow copy?
        So, why does it "work" on the first pass (without using clone) and then from there I just get a shallow copy?

        Short answer: it did *not* "work" on pass#1 any differently than pass#2 or pass#3. That fact is might not be evident from Data::Dumper output until one obtains mastery of Perl references (via perlreftut and perlref).

        Detailed explanation:
        If you say

        my @foo = ( 1, 2, 3 ); my $arrayref = \@foo;
        , and you wanted to explain to a fellow programmer what $arrayref pointed to, you would (of course) simply say "the array named @foo".

        If instead you had coded

        my $arrayref = [ 1, 2, 3 ];
        , then $arrayref would still point to equivalent data, but you could *not* describe it as a reference to a particular array named @something. The array is nameless, floating in memory as long as anyone has a reference to it, and vanishing once the last reference goes away. It is anonymous.

        Furthermore, if I say

        my @foo = ( 1, 2, 3 );
        , then I would be (close enough to) accurate to say that the three numbers "live" in @foo, and that the 2 is stored between the 1 and the 3. If instead I code
        my @foo = ( [1], [2], [3] );
        , I would be far less accurate to say that the three anonymous arrays "live" in @foo, or that the anonymous array containing 2 is stored between the anonymous array containing 1 and the anonymous array containing 3.

        That distinction is important. Arrays cannot hold arrays or hashes; Arrays can only contain scalars. Hash values can only contain scalars. The three anonymous arrays "live" out in space, only the *references* to them get stored (in an ordered fashion) in the @foo array.

        When Data::Dumper says "$VAR1->[0]{'sizes'}" in your second two passes, it does so because that string of code is the closest thing to a name it has to describe the anonymous hash it already described in the first pass. Data::Dumper cannot print simple repetitions of the anonymous hash from the first pass, because that would be describing 3 separate anonymous hashes, each (coincidentally) containing the same data elements, yet each floating in its own separate place in memory.

        Perhaps this simpler example will help.
        my $arrayref1 = [ 9, 10, 'a big fat hen' ]; my $arrayref2 = [ $arrayref1, $arrayref1, $arrayref1, ]; print "Only showing super-structure:\n"; print Dumper $arrayref2; print "Showing both sub-structure and super-structure:\n"; print Dumper $arrayref1, $arrayref2;
        produces this output:
        Only showing super-structure: $VAR1 = [ [ 9, 10, 'a big fat hen' ], $VAR1->[0], $VAR1->[0] ]; Showing both sub-structure and super-structure: $VAR1 = [ 9, 10, 'a big fat hen' ]; $VAR2 = [ $VAR1, $VAR1, $VAR1 ];
Re^5: Strange hash array behavior
by Util (Priest) on Jan 29, 2009 at 06:27 UTC
    And yet an even better example... why the heck does this work and not the previous examples?
    It only *seems* to work, because you are dumping too little, too often.
    If you add print Dumper($data1); after the close of the foreach, you will see the $VAR1->[0]{'sizes'} back-pointers.