Perl logo


Perl Refreshments ⌘

References #1.0

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.

They are similar to C pointers - it holds the actual memory address of whatever it points to.

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.

Why use a reference?

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.

How do we make a reference?


  1. put a \ (backslash) in front of a variable
  2. use { } or [ ] to make references to an anonymous hash or array

Method 1

$aref = \@array; # $aref now holds a reference to @array
$href = \%hash; # $href now holds a reference to %hash

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.

Anonymous references contain actual data, without creating a variable:
$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!

$xy = $aref; # $xy now holds a reference to @aref
$p[3] = $href; # $p[3] now holds a reference to %href
$z = $p[3]; # $z now holds a reference to %

How do we use a reference?


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}.

That syntax is a bit odd.
Think of it as the array referred to by $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.

  • @a ==> @{$aref} An array
  • reverse @a ==> reverse @{$aref} Reverse the array
  • $a[3] ==> ${$aref}[3] An element of the array
  • $a[3]=17 ==> ${$aref}[3]=17 Assigning an element
  • 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.

    Reference to a hash

    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/;}
    

    ref2.png

    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/;
    	}
    }
    

    ref1.1.png

    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);
    	}
    }
    

    ref1.2.png

    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


    -30-