references are a data structure added to Perl in version 5. They work with hashes, arrays and subroutines, allowing us to build and access some very complicated data structures.
A reference is a scalar value, but it refers to an entire hash or array (as well as other things). Consider them like names for arrays or hashes. This will make more sense when we talk about how to use references.
A referent is the value that lives at the address (reference). It can be any built-in type (scalar, array, hash, ref, code, or glob), or some user-defined type based on one of these.
This is what now allows us to have hashes of references to arrays, hashes of references to hashes, arrays of references to arrays, etc. These are typically shortened to 'hash of arrays', 'hash of hashes', 'array of arrays' (multi-dimensional data structures), etc.
This question has been asked (several times) in web forums and newsgroup postings. Generally, references allow us to create and use multi-dimensional arrays and hashes. They also use less memory, and generally run faster, if you need to pass an array or hash to a subroutine.
References are also crucial in making and using complex data structures.
Method 1
Method 2
This method is useful when you don't want or need a name for the hash or array ...
... essentially a short cut to first making a hash / array, then making a reference to it. The named hash / array is not created.
In the 2nd case above, $aref and $href makes an anonymous array / hash, and returns the reference to it.
$ArrayRef=[2,3,5,7,11,13,17]; # anonymous array reference $HashRef={ # anonymous hash reference "Fred" => "Wilma", "Clyde" => "Bonnie", "John" => "Yoko", "Romeo" => "Juliet", };Note the use of the $ sigil in both cases - references are scalar.
Being a scalar variable, a reference can now be copied or stored like any other scalar!
When you 'use' or access a reference, it is called dereferencing.
You, as the programmer, have the responsibility to explicitly dereference a hash, array, or subroutine if it holds a reference. Perl does not do this for you.
Now the fun begins. Recall how we said that references are like names for arrays or hashes? This means exactly that - where you would normally use @a, you would now use @{$aref}.
If $aref contains a reference to an array, then you can put {$aref} anywhere you would normally put the name of an array. For example, @{$aref} instead of @array.
Here is a simple example to see how it works ...
my @numbs=(0,1,2,3,4,5,6,7,8,9); # <--- an array my $numbsRef=\@numbs; # <--- hard ref to the array print "@{$numbsRef}" . "\n"; # <--- print the reference to the array print "The 3rd element in the array is: ${$numbsRef}[2]\n";
Hash references are used the same way: where you would use %hash for a hash, you would use %{$href} to use the reference to it.
Note that in both cases, the name of the reference is wrapped in braces.
To demonstrate a hash of hashes (hash of references to a hash), I will use some familiar data: months of the year. We want to have the full month names, their 2-letter abbreviations, and their 2-digit month number.
To do this I'll use the 2nd method described above, using braces { } to create a hash of anonymous hashes.my $monthsRef = { "03" => { short => "MR", long => "March", }, "01" => { short => "JA", long => "January", }, "04" => { short => "AP", long =>"April", }, "06" => { short => "JE", long => "June", }, "02" => { short => "FE", long => "February", }, "05" => { short => "MY", long => "May", }, "07" => { short => "JL", long => "July", }, "12" => { short => "DE", long => "December", }, "09" => { short => "SE", long => "September", }, "08" => { short => "AU", long => "August", }, "10" => { short => "OC", long => "October", }, "11" => { short => "NO", long => "November", }, };
To clarify:
We hope you note that the original order of the hash is NOT in any particular order. But we sort the hash on keys - that is why I chose to do it this way.
Recall that a hash is 'an unordered set of scalars'. So we have to sort it anyway.
The structure of your hash depends on how you intend to use it. In this case I decided to use the numeric value of each month as the key, so it could be sorted in the right 'monthly' order. Using the month names as keys, would not allow us to 'sort' them properly.
Here is how we print this structure:
foreach my $k (sort keys %{$monthsRef}) { print qq/${$monthsRef}{$k}{short} \t $k \t ${$monthsRef}{$k}{long}\n/; }
Note: I used a front-slash / to wrap my quoted string, to differentiate it from the braces around the hash variables. Normally I would use a pair of braces { }.
To avoid the use of ${$, we could have used the infix operator -> :
foreach my $k (sort keys $monthsRef) { print qq/$k \t $monthsRef->{$k}->{short} \t $monthsRef->{$k}->{long}\n/;}
If you're wondering why I chose 2-letter abbreviations instead of the normal 3-letter ones, I used to work in a university library using the LC (Library of Congress) cataloguing rules. 2 letters = less typing.
As another example, what if we want to print only the even-numbered months? This will require a twist so pay attention!
To get even-numbered months we can use the modulus operator. It returns the value of a division by another number, with no remainder. The absolute value of the number. Since we want even-numbered months, we can use the month number % 2.
Yes, that's a percent sign. In perl it's the modulus operator. The way it is used here means 'return the modulus when divided by 2'. Thus any even number should return 0.
print "Even-numbered months ...\n"; foreach my $k (sort keys %{$monthsRef}) { if ( ($k % 2 == 0) ) { print qq/$$monthsRef{$k}{long} \t $$monthsRef{$k}{short} \t $k\n/; } }
Well, that worked but we need to fix the display. I use a tab \t to make the columnar output. For some of the months we need another tab character.
A tab character is typically 8 characters wide, so we need to check the length of month names against that.
In fact we have to use a value of 7 for our comparison, as shown below ...
print "Even-numbered months ...\n"; foreach my $k (sort keys %{$monthsRef}) { my $mLength = length($$monthsRef{$k}{long}); if ( ($k % 2 == 0) ) { print qq/$$monthsRef{$k}{long} \t $$monthsRef{$k}{short} \t $k\n/ if ( $mLength >= 7); print qq/$$monthsRef{$k}{long} \t\t $$monthsRef{$k}{short} \t $k\n/ if ( $mLength < 7); } }
Much better!
The twist mentioned earlier is getting the length of the month name.
You would think that something like this would work ...
print qq/$$monthsRef{$k}{long} \t\t $$monthsRef{$k}{short} \t $k\n/ if length($$monthsRef{$k}{long} >= 7);
Unfortunately, no. We have to put the length result into a scalar value as in the line 'my $mLength = length($$monthsRef{$k}{long});'
Then depending on that result, print the line correctly.
As you can see (I hope), references used in this way can result in some very complex records.
For the official documentation visit the Perl::Doc site.
Next up -- More Hash of Hashes