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
Common mistake: storing the count instead of the elements#
Assigning an array to a single scalar slot evaluates the array in scalar context, which yields its element count, not its contents:
for my $i (1 .. 10) {
my @array = somefunc($i);
$AoA[$i] = @array; # WRONG - stores the count
}
After this loop every $AoA[$i] is a number. To store the elements, wrap them in an anonymous-array constructor so you get a reference to a fresh copy:
$AoA[$i] = [ @array ]; # right - a new array, copied
If you genuinely wanted the count, say so with scalar and a name that reflects it:
$counts[$i] = scalar @array; # explicit - no surprise later
Common mistake: one reused array behind every reference#
The repeated-array trap from the previous section also appears when the shared array is reused across loop iterations:
our @array;
for my $i (1 .. 10) {
@array = somefunc($i);
$AoA[$i] = \@array; # WRONG - same array every time
}
Every \@array is a reference to the same array, so all ten slots end up pointing at whatever somefunc returned last. Again, [ @array ] fixes it by copying.
Declaring the array with my inside the loop is safe, however:
for my $i (1 .. 10) {
my @array = somefunc($i);
$AoA[$i] = \@array; # fine - fresh array each iteration
}
my @array builds a new array on every pass, so each \@array refers to a distinct one. The reference outlives the loop body because the array it points at stays alive as long as the reference does. The constructor form [ @array ] is still the clearer choice; reach for the \@array form only when you understand why it works here.
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.