
We finished last week with an example of solving passing multiple arrays to a subroutine by passing references to arrays rather than the arrays themselves. You may not have noticed, but in doing so we made use of a multidimensional array (though only briefly). When we pass arguments to a subroutine, the arguments are stored in the @_ array — so when we pass array references we’ve actually made an array of arrays (well, an array of array references — but that’s what a two dimensional array is in Perl). Look at this example:
my @one = (9,8,7);
my @two = (6,5,4);
foo(\@one, \@two);
sub foo {
print "$_[1][1]\n";
}
The above prints 5 (the second element of the second array). Ignoring how the dereference works for a minute, let’s look at a similar example not involving subroutines:
my @one = (9,8,7);
my @two = (6,5,4);
my @rows = (\@one, \@two);
print "$rows[1][1]\n"; # prints: 5
The same thing is going on here except that we are explicitly assigning to an array @rows rather than implicitly assigning to the special @_ array (which holds subroutine arguments). Now, let’s return to dereferencing to figure out why this works as it does.
We have already seen two ways of dereferencing an array reference:
my @array = (42, 13, 12);
my $aref = \@array;
print "${$aref}[1]\n"; # explicit
print "$$aref[1]\n"; # shortcut
An alternate method is to use the "arrow" dereference syntax:
my @array = (42, 13, 12);
my $aref = \@array;
print "$aref->[1]\n";
In this method, perl assumes that whatever is to the left of the "arrow" resolves to a reference, so perl dereferences it and then returns the value given by the subscript on the right. This works for hashes as well:
my %hash = (name => 'andrew', beer => 'dark-ale');
my $href = \%hash;
print "$href->{beer}\n";
Now, we can return to the two dimensional array example and use the arrow syntax:
my @one = (9,8,7);
my @two = (6,5,4);
my @rows = (\@one, \@two);
print "$rows[1]->[1]\n"; # prints: 5
Here, everything to the left of the arrow ($rows[1]) resolves to a reference, and then we look up the [1] subscript in that array. Because Perl has no multidimensional arrays, Perl knows that ordinarily an expression like: $rows[1][1] wouldn’t make sense. So, Perl assumes that there is always an implicit arrow between any two subscripts — this is why $rows[1][1] actually works, because internally Perl assumes it to mean $rows[1]->[1].
The same is true for hash references and any mixture of hash and array references:
my @beer = ('dark-ale', 'pale-ale', 'stout');
my %hash = ( name => 'andrew', beer => \@beer);
print "$hash{name}'s favorite beer is $hash{beer}[0]\n";
print "$hash{name} will accept: @{$hash{beer}}\n";
In the above, the $hash{beer}[0] is the same as: $hash{beer}->[0]. The thing to the left of the array resolves to an array reference and the subscript on the right gets the value at that index. Now, in the second print statement we had to use the extra curly braces to dereference to entire array reference — we could not just do:
@$hash{beer}
Because Perl assumes that $hash is the reference (we don’t even have a $hash variable in our example) instead of $hash{beer}. In other words, without the curly braces, it is parsed as:
@{$hash}{beer}
which isn’t what we wanted. So we have to group the expression that actually resolves to the reference inside of curly braces
@{ $hash{beer} }
and then perl knows exactly what we are trying to dereference. Creating multidimensional structures is not difficult — but Perl gives another very useful convenience called anonymous arrays and hashes that we will look at next week.
Next Week: Understanding References (part 4: anonymous structures)
*****