The whole mystery with multi-dimension structures in perl is quite easy to understand once you realize that there are only three types of variables to deal with. Scalars, arrays and hashes.
- A scalar is a single value, it can contain just about anything, but
only one at the time.
- An array contains a number of scalar values, ordered by a fixed
numerical index.
- A hash contains scalar values, indexed by keys made of strings.
And all arrays, hashes or scalars act this way. Multi-dimension arrays are no different from single dimension.
This is also expressed very succinctly in perldata:
All data in Perl is a scalar, an array of scalars, or a hash of
scalars. A scalar may contain one single value in any of three
different flavors: a number, a string, or a reference. In general,
conversion from one form to another is transparent. Although a scalar
may not directly hold multiple values, it may contain a reference to
an array or hash which in turn contains multiple values.
For example:
my @array = (1, 2, 3);
Here, $array[0] contains 1, $array[1] contains 2, etc. Just like you would expect.
my @aoa = ( [ 1, 2, 3 ], [ 'a', 'b', 'c' ] );
Here, $array[0] contains an array reference. If you print it out, it will say something like ARRAY(0x398a84). Don't worry! That's still a scalar value. How do we know this? Because arrays can only contain scalar values.
When we do something like
for $aref ( @AoA ) {
print $aref; # prints ARRAY(0x398a84) or similar
}
It's no different from doing
for $number ( @array ) {
print $number;
}
$aref and $number are scalar values. So far, so good. Take a moment and lock this knowledge down: Arrays can only contain scalar values.
Now, the next part is simply knowing how to deal with references. This is documented in perlref and perlreftut.
A reference is a scalar value. It's an address to a location in memory. This location contains some data. In order to access the actual data, we need to dereference the reference.
As a simple example:
my @data = (1, 2, 3);
my $aref = \@data; # The backslash in front of the sigil creates a reference
print $aref; # print something like ARRAY(0xa4b6a4)
print @$aref; # prints 123
Adding a sigil in front of the reference tells perl to dereference the scalar value into the type of data the sigil represents. In this case, an array. If you choose the wrong sigil for the type of reference, perl will give an error such as:
Not a HASH reference
In the example above, we have a reference to a specific, named location. Both @$aref and @data access the same values. If we change a value in one, both are affected, because the address to the memory location is identical. Let's try it:
my @data = (1, 2, 3);
my $aref = \@data;
$$aref[1] = 'a'; # dereference to a scalar value by $ sigil
# $aref->[1] = 'a' # does the same thing, a different way
print @data; # prints 1a3
print @$aref; # prints 1a3
We can also have anonymous data. If we were only interested in building an array of arrays, we'd have no interest in the @data, and could skip it by doing this:
my $aref = [ 1, 2, 3 ];
The brackets around the list of numbers create an anonymous array. $aref still contains the same type of data: A reference. But in this case, $aref is the only way we have of accessing the data contained at the memory location. Now, let's build some more scalar values like this:
my $aref1 = [ 1, 2, 3 ];
my $aref2 = [ 'a', 'b', 'c' ];
my $aref3 = [ 'x', 'y', 'z' ];
We now have three scalar variables that contain references to anonymous arrays. What if we put these in an array?
my @aoa = ($aref1, $aref2, $aref3);
If we'd want to access $aref1, we could do print @$aref1, but we could also do
print @{$aoa[0]};
In this case, we need to use the extended form of dereferencing: @{ ... }. Because perl does not like ambiguity, it requires us to distinguish between @{$aoa[0]} (take the reference in $aoa[0] and dereference as an array) and @{$aoa}[0] (take the reference in $aoa and dereference as an array, and take that arrays first value).
Above, we could have used @{$aref}, as it is identical to @$aref.
So, if we are only interested in building an array of arrays, we are not really interested in the $aref1 scalars either. So let's cut them out of the process:
my @aoa = ( [ 1, 2, 3 ], [ 'a', 'b', 'c' ], [ 'x', 'y', 'z' ]);
Tada! That's an array of arrays.
Now, we can backtrack. To access the values inside this array, we can do
for my $scalar ( @aoa ) {
print @$scalar; # prints 123abcxyz
}
This time, I used a different variable name, just to make a point. This loop takes each value from @aoa -- which still is only a scalar value -- dereferences it as an array, and prints it.
Or we can access @aoa via its indexes
for my $i ( 0 .. $#aoa ) {
print @{$aoa[$i]};
}
And that's all there is to it!