Arrays of arrays#

Perl arrays hold scalars, and scalars cannot directly contain other arrays. To build a two-dimensional array — a matrix, a grid, a table of rows — you store references to inner arrays as the outer array’s elements. This chapter shows how.

Building a matrix#

The most direct form uses anonymous array constructors ([...], covered in detail in Anonymous references) so you don’t have to name every inner array:

my @m = (
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
);

@m has three elements. Each element is a scalar holding an array reference. There are four arrays in play: the outer @m and three anonymous inner arrays.

Reading and writing elements#

$m[1] is the reference to the second row. To reach the third element of that row, apply the arrow:

$m[1]->[2]             # 6
$m[0]->[0]             # 1
$m[2]->[2] = 99;       # overwrite the bottom-right cell

Between two subscripts, the arrow is optional. This rule is what makes multidimensional access readable:

$m[1][2]               # same as $m[1]->[2]
$m[2][2] = 99;         # same as $m[2]->[2] = 99

The arrow remains required between a scalar variable and its first subscript: $aref->[0] cannot be shortened to $aref[0] because that would mean “first element of @aref”. The shortcut applies only inside a chain of subscripts.

For three dimensions it’s the same idea:

$cube[2][3][5]         # readable
${${$cube[2]}[3]}[5]   # the same expression fully spelled out

The second form is what you’d have to write without the arrow shortcut. Nobody does that.

Iterating over a matrix#

To walk rows, loop over the outer array; each iteration gives you an array reference. To walk cells, dereference that reference inside the loop:

for my $row (@m) {
    for my $cell (@{$row}) {
        print "$cell ";
    }
    print "\n";
}

If you also need the indexes, use a counted loop on the outer array and on the dereferenced inner:

for my $i (0 .. $#m) {
    for my $j (0 .. $#{$m[$i]}) {
        print "m[$i][$j] = $m[$i][$j]\n";
    }
}

$#{$m[$i]} is the last-index operator applied to the dereferenced inner array. It’s the $#array form with {$m[$i]} standing in for the array name.

Growing rows and columns#

Append to a row by pushing into the inner array via a reference:

push @{$m[0]}, 10;     # row 0 is now (1, 2, 3, 10)

Append a whole row to the matrix by pushing an array reference onto the outer array:

push @m, [10, 11, 12]; # @m now has four rows

Both operations use push — once on a dereferenced inner array, once on the outer array directly.

Autovivification#

Assigning to $m[$i][$j] works even if $m[$i] doesn’t exist yet:

my @grid;
$grid[5][7] = 'X';

This creates @grid with six empty slots and one reference in slot 5. That reference points at a fresh anonymous array whose eighth slot holds 'X'. Perl generates both the outer slot and the inner array automatically. This is called autovivification and is the reason you almost never have to write $m[$i] = [] before using it.

Autovivification fires on assignment and on dereferencing in lvalue context. Plain reads do not autovivify:

my @grid;
my $x = $grid[5][7];   # $x is undef; no inner array created
exists $grid[5];       # false

Use defined to tell the “never existed” case from the “exists and holds undef” case, and exists to check the slot itself without autovivifying.

Common mistake: sharing a single inner array#

This looks like three rows but is actually one row repeated:

my @inner = (0, 0, 0);
my @bad   = (\@inner, \@inner, \@inner);

$bad[0][1] = 9;
print "$bad[2][1]\n";  # 9 — they're all the same array

To get three independent rows, construct each one separately. The anonymous-constructor form handles this by always allocating fresh:

my @good = ([0, 0, 0], [0, 0, 0], [0, 0, 0]);
$good[0][1] = 9;
print "$good[2][1]\n"; # 0

A loop over map gives the same result for variable row lengths:

my @grid = map { [ (0) x 10 ] } 1 .. 10;  # 10×10 of zeros

Where to go next#

  • Hashes and mixes — hashes of arrays and arrays of hashes, the two shapes that cover most real-world records.

  • Anonymous references — the [...] / {...} constructors used above, in detail.