Multidimensional Arrays in Perl
June 22, 2012 Leave a comment
My last post dealt with the technique of using a hash to pass named parameters to a subroutine. Since I covered the hash data structure last time, it seemed appropriate to spend some time on arrays. Using a one dimensional array in Perl is straight forward and routine practice -not very exciting. On the other hand, multidimensional arrays are quite interesting, because Perl doesn’t really support them. They have to be simulated using pointers.
In Perl a reference to an array, hash or scalar can be created using a backslash (\). For example:
my $scalar=1.50; my @array=qw(one two three); my %hash=(a=>'1', b=>'2', c=>'3'); # Create references... my $ref1=\$scalar; my $ref2=\@array; my $ref3=\%hash;
We can de-reference, or access the data the reference points to, by prefixing the reference variable with the data type symbol. So for example, to de-reference an array, use @$ref2. Or for a hash, use %$ref3. In the case of arrays, use $ref2->[n] to access a specific element’s value, where n is an index value. And for a hash, use $ref3->{key}.
Continuing from the script started above, we can print the original values using these de-referencing techniques:
print "Scalar: " . $$ref1 . "\n";
print "Array: " . join(',', @$ref2) . "\n";
print "Hash: ";
for (sort keys %$ref3) { print $_ . '=>' . $ref3->{$_} . " " };
print "\n";
This produces the following:
Scalar: 1.5 Array: one,two,three Hash: a=>1 b=>2 c=>3
“Now we know how to create and use references, but what does this have to do with multidimensional arrays?”, you ask. Well, imagine what we could do with an array of pointers, or references, to additional arrays. Think an array of arrays.
Let’s say we we want to create a matrix of 2 rows and 10 columns. The cells of the matrix will be filled with the alphabet, so that row 0, column 5 returns the letter ‘F’.
0 1 2 3 4 5 6 7 8 9 0: A B C D E F G H I J 1: K L M N O P Q R S T
Here’s the code to create and print a two dimensional matrix like the one above:
#!/usr/bin/perl -w
use strict;
my @chars=qw(A B C D E F G H I J K L M N O P Q R S T);
my @array1=@chars[0..9];
my @array2=@chars[10..19];
my @array3;
my @innerArray;
my $i;
my $j;
push(@array3, \@array1, \@array2);
# Print our column header row
print " ";
for $i (0..9) { print ' ' . $i }
print "\n";
# Print the conents of array3 using a technique not reliant on
# knowing the size of any of our arrays.
for $i (0..$#array3) {
print $i . ": " . join(' ', @{$array3[$i]}) . "\n";
}
# And the value of row 0 column 5 is...
print "\n(0,5)= " . $array3[0]->[5] . "\n";
A few notes… A range can be used to split an array. Doing it properly requires referencing the array with @ rather than $. References to array1 and array2 are pushed onto array3, making array3 an array of arrays. The ‘for’ and ‘foreach’ statements are interchangeable when looping through a range. When looping through an array, never hard code or assume the number of elements is known. Use $#arrayname to get the last index number, or use @arrayname to operate on the entire array. Notice how braces are used in @{$array3[$i]} to de-reference the inner array.
Running the script produces the following:
0 1 2 3 4 5 6 7 8 9 0: A B C D E F G H I J 1: K L M N O P Q R S T (0,5)= F
Who says you can’t do multidimensional arrays in Perl? It might not be the most modern language out there, but look at how much work we were able to do with only a few lines of code. Take away the formatting and title we added to the grid, and there’s not much left. Perl makes doing the complex simple.