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

Hi. I'm a relatively new Perl programmer, and I'd like some help working with two arrays of arrays. I want to store a value from the first one, @AoA1, into the second one, @AoA2, without changing @AoA1. At first I was trying $AoA2[foo][bar] = $AoA1[foo][bar]. Now, I don't even really understand what's happening when I use the [][] indexing - I think I end up assigning a reference instead of a value - but the effect is that when I change that value of @AoA2 again it changes the corresponding value of @AoA1, which is not what I want. So how do I dereference the value I want from @AoA1?

Replies are listed 'Best First'.
Re: accessing element of array of arrays
by FunkyMonk (Bishop) on Jul 05, 2008 at 07:55 UTC
    Assignment between array elements should work just as you described, so I think there's more going on than you think. Can you post a brief but runnable demonstration of the effect you're seeing?

    Unless I state otherwise, all my code runs with strict and warnings
      Ok:
      #!/usr/bin/perl use strict; use warnings; my @AoA1 = ([1, 'a'], [2, 'b']); my @AoA2; push @AoA2, @AoA1; $AoA2[0][0] = $AoA1[0][0]; print "$AoA1[0][0], $AoA2[0][0]\n"; $AoA2[0][0] ++; print "$AoA1[0][0], $AoA2[0][0]\n";
      Notice that when $AoA2[0][0] is augmented, $AoA1[0][0] is as well. The desired output would be:
      1, 1 2, 1
      instead of
      1, 1 2, 2
      .
        In Perl, an array of arrays is really an array of references to arrays. You can see that by printing @AoA1:
        say @AoA1; # ARRAY(0x606df0)ARRAY(0x621e70)
        When you push @AoA2, @AoA1; you're pushing the same references on to @AoA2:
        say @AoA2; # ARRAY(0x606df0)ARRAY(0x621e70)
        The hex numbers are the same, indicating the both refer to the same area of memory.

        So, to answer your original question, changing one of the array elements, will also change the corresponding element in the other array. To get round that, you could use

        • the freeze & thaw functions from the core module Storable
        • the CPAN module Clone
        • some Perl
        use Clone 'clone'; my @AoA3 = @{ clone( \@AoA1 ) }; say @AoA3; # ARRAY(0x7c46c0)ARRAY(0x7c4720) use Storable qw/freeze thaw/; my @AoA4 = @{ thaw( freeze( \@AoA1 ) ) }; say @AoA4; # ARRAY(0x7095c8)ARRAY(0x709718) my @AoA5 = map { [ @$_ ] } @AoA1; say @AoA5; # ARRAY(0x67ff58)ARRAY(0x7e8468)
        Storable and Clone will work with any data structure but the pure perl method will need to be tailored to each specific data structure.

        Unless I state otherwise, all my code runs with strict and warnings
Re: accessing element of array of arrays
by jethro (Monsignor) on Jul 05, 2008 at 13:14 UTC
    Two comments, two opinions. Lets test whether FunkyMonk is right:

    perl -e ' $A[5][2]= 33; $B[1][1]=4; $B[5][2]= $A[1][1]; $B[1][1]=9; p +rint "A=$A[5][2], B=$B[1][1]\n";' A=33, B=9
    You see, your code should be working, like FunkyMonk predicted. You have a bug somewhere else

    To understand what [][] means, it is just a short form of []->[]. So you really have only pointers to arrays in each $AoA1[...]. Exactly as in C.

      Your code doesn't seem to test what the author is discussing -- namely, after your assignment $B[5][2] = $A[1][1], does changing $A[1][1] change $B[5][2]? (Indeed, the only 'entangled' elements of your code are $B[5][2] and $A[1][1], and they aren't logged at the end.) With your choice of indexing, I think that what you want is:
      perl -e '$A[1][1] = 33; $B[5][2] = $A[1][1]; $A[1][1] = 9; print "A = +$A[1][1], B = $B[5][2]\n";'
      This prints out
      A = 9, B = 33
      which is exactly what (I think) you meant, and which certainly seems to be the behaviour desired by the original poster.

      UPDATED (twice) -- I flipped the values of A and B (though I could have sworn I'd cut-and-pasted from the shell), and fixed a mark-up error.

Re: accessing element of array of arrays
by jhourcle (Prior) on Jul 05, 2008 at 14:06 UTC
    I want to store a value from the first one, @AoA1, into the second one, @AoA2, without changing @AoA1 ... when I change that value of @AoA2 again it changes the corresponding value of @AoA1

    I'd have to ask -- what's the value that you're assigning? It is a scalar, or a reference? If it's a reference, you're always going to see the exact behavior that you describe, unless you dereference it when assigning it in @AoA2 ... but how you dereference is dependent upon what's in there to start with. (eg, %{$AoA1[0][1]} or ${$AoA1[0][1]} or @{$AoA[0][1]})

    I'd suggest using Data::Dumper to take a look at just what's in @AoA1, and what's actually getting assigned in @AoA2.

      Yeah, so a reference is being assigned to @AoA2, which is why it's changing @AoA1 when edited. I still don't know how to dereference the value in @AoA1 before assigning it to @AoA2 (it's a scalar). When I tried ${$AoA1[0][1]} it said: "Can't use string ("1") as a SCALAR ref while "strict refs" in use." String ("1") is the scalar value at @AoA1[0][1]).
        How can you dereference the value in $AoA1[0][1] when $AoA1[0][1] is obviously a scalar (with the value of 1 it seems) ?

        You can only dereference a reference. '1' is not a reference

        Please post more of your code. Language is always ambiguous, code (mostly) not.For example your statement "a reference is being assigned to @AoA2" is ambiguous at least and if taken at face value definitly not what you want. @AoA2= \$x; is equivalent to @AoA2= (); $AoA1[0]= \$x;. Makes no sense, right?

        Also you might use Data::Dumper to get a clear picture of the data you are operating on (really Data::Dumper is the most often mentioned module on PerlMonks, and that's justified).

        What about:

        $AoA2[0] = [ @{ $AoA1[0] } ];

        Dereference first element of @AoA1 as an array; create a new reference to an anonymous array containing the result of the dereferenced array, and assign that new reference as first element of @AoA2.

Re: accessing element of array of arrays
by Lawliet (Curate) on Jul 05, 2008 at 08:24 UTC
    I am not really sure about what you are trying to accomplish, but would push @AoA2, $AoA1[0] work?

    Note that the number inside the brackets is the element number of $AoA1 you want to push into the second array.

      Beware of that code!

      With that code you push the reference in $AoA[0] to @AoA2 (let's say it is stored at $AoA2[0]). So if you change the data referenced by $AoA2[0] you will change the data also for @AoA1, because they ($AoA1[0] and $AoA2[0]) share the reference to the same data!

      It may be that that is you desired behaviour, but it may be not...

      #!/usr/bin/perl # vi:ts=4 sw=4 et: use strict; use warnings; use Data::Dumper; my @AoA1 = ( [ 1, 2, 3 ], [ qw( a b c ) ], ); my @AoA2 = (); $AoA2[0] = $AoA1[0]; # change the '1' in AoA2 $AoA2[0]->[0] = 0; # check stored reference print "\$AoA1[0]", $AoA1[0], $/; print "\$AoA2[0]", $AoA2[0], $/; # look what happened! AoA1 is changed! print Dumper \@AoA1;