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

I was sent the following code (slightly edited):
#!/usr/bin/perl # fill two 25 x 25 arrays with zeros, then modify a couple of cells my @PP = my @OO = (1..25); # fill PP and OO arrays with zeros for($rr = 0; $rr < 25; $rr++) { for($cc = 0; $cc < 25; $cc++) { $PP[$rr][$cc] = 0; $OO[$rr][$cc] = 0; } } # uncommenting the below shows array PP zeroed out for my $row (@PP) { print join(",", @{$row}), "\n"; } print "\nAfter:\n"; $OO[1][1]=1; # just so it will be easy to spot $OO[2][2]=2; $OO[3][3]=3; $OO[4][4]=4; # code below. shows the values inserted into array OO cells appearing +in PP cells ??? print "PP array\n"; for my $row (@PP) { print join(",", @{$row}), "\n"; } print "\nOO array\n"; for my $row (@OO) { print join(",", @{$row}), "\n"; } exit;
When run, both arrays are the same. With the first active line changed to my @PP = my @OO; with no initialization, the code works as expected; i.e., the @PP array is unchanged. Why the difference??

Replies are listed 'Best First'.
Re: Array wierdness
by Corion (Patriarch) on Apr 25, 2022 at 13:56 UTC

    This seems to be a bug surprising side-effect of non-strict code to me. First you assign the numbers to both arrays in one go. Then, you use these numbers as (symbolic) references when you by using the arrays as multi-dimensional. Perl then uses the arrays @1, @2 etc. , and these arrays are shared between @PP and QQ. This is surprising.

    Note that using 'strict' would have allowed Perl to tell you that you are implicitly upgrading/changing numbers to references:

    Can't use string ("1") as an ARRAY ref while "strict refs in use at .. +.

    Update: Updated my explanation to what actually happens, after a conversation with haarg

      Thanks for the explanation. FWIW, assigning the numbers to the arrays in separate lines makes no difference.
        Just to elaborate a bit more on this, consider the code:
        my @PP = ("foo", "bar", "baz"); my @OO = ("AAA", "bar", "CCC"); $PP[1][0] = "foobar";
        What happens is that this does not modify the @PP array, instead it uses the value of $PP[1] as an array reference which points to the global(/package) variable @bar. To make this a bit clearer:
        #!/usr/bin/perl -l my @PP = ("foo", "bar", "baz"); my @OO = ("AAA", "bar", "CCC"); $PP[1][0] = "foobar"; print "\@PP: " . join(", ", @PP); print "\@OO: " . join(", ", @OO); print "\@bar: " . join(", ", @::bar);
        Output:
        @PP: foo, bar, baz
        @OO: AAA, bar, CCC
        @bar: foobar
        
        The '@PP' and '@OO' array are unmodified; the '@bar' array however was created.

        With use strict; you get the error: Can't use string ("bar") as an ARRAY ref while "strict refs" in use

        FWIW, assigning the numbers to the arrays in separate lines makes no difference.

        This is because your code's comments are wrong:

        # fill two 25 x 25 arrays with zeros, then modify a couple of cells my @PP = my @OO = (1..25);

        This does not create 25x25 arrays, this creates 1x25 arrays.

        Below shows a 1x25 (what you have) vs a 2x5:

        use strict; use warnings; use Data::Dump; my @one_by_twentyfive = (1..25); my @two_by_five = ([1..5],[1..5]); dd \@one_by_twentyfive; dd \@two_by_five; __END__ [1 .. 25] [[1 .. 5], [1 .. 5]]

        or this one populates a 25x25 array, then overrides 3 slots:

        use strict; use warnings; use Data::Dump; my @pp; push @pp, [1..25] for 1..25; dd \@pp; $pp[1][1] = 1; $pp[2][2] = 2; $pp[3][3] = 3; dd \@pp; __END__ [ [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], ] [ [1 .. 25], [1, 1, 3 .. 25], [1, 2, 2, 4 .. 25], [1, 2, 3, 3, 5 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], [1 .. 25], ]
        short answer

        print this on a t-shirt:

        and wear it at work. ;-)

        long answer

        $PP[1][2]=3 is a shorter notation for $PP[1]->[2] = 3

        so with

        $PP[1] == $OO[1] == 1

        what you are effectively doing is

        1->[2] = 3

        which is just populating @1

        debugger-demo perl -de0

        DB<46> 1->[2] = 3 DB<47> x @1 0 undef 1 undef 2 3 DB<48>

        For good reasons that's forbidden under strict

        DB<45> use strict; my @PP; $PP[1]=1; $PP[1][2] = 3 Can't use string ("1") as an ARRAY ref while "strict refs" in use at ( +eval 66) ...

        > FWIW, assigning the numbers to the arrays in separate lines makes no difference.

        Well I hope it's now clearer why...

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: Array wierdness
by BillKSmith (Monsignor) on Apr 27, 2022 at 16:03 UTC
    It may help you to consider that there is no such thing as a "25 x 25 array" in Perl. Of course, we can simulate it with a structure called an "array of array refs" (AoA for short). The subscript notation that you use (documented as an alternate way to dereference a reference)hides this from a casual reader of our code. This works so well that we all tend to ignore the difference. "use strict; is your friend because it lets us know when we forget the subtle difference and accidently create symbolic references.
    Bill
Re: Array wierdness
by etj (Priest) on Apr 27, 2022 at 16:04 UTC
    Since the aim seems to be to have rectangular data, it seems worth mentioning PDL which is designed for operating on numerical data using very powerful "array programming" techniques.