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

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.

Replies are listed 'Best First'.
Re^6: Strange hash array behavior
by Rodster001 (Pilgrim) on Jan 29, 2009 at 06:14 UTC
    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 ];
        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.
        Ok, I am getting my head around this now... thanks for taking the time to explain this in such detail. Much appreciated... Cheers!