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.