| home / programming / perl / learning / chap3 / 1 | [previous][next] |
|
|
Among its many other meanings, the backslash (\)
character is also the "take a reference to" operator. When you use
it in front of an array name, e.g., \@skipper,
the result is a reference to that array. A reference
to the array is like a pointer: it points at the array, but is not the array
itself.
A reference fits wherever a scalar fits. It can go into an element of an array or a hash, or into a plain scalar variable, like this:
my $reference_to_skipper = \@skipper;
The reference can be copied:
my $second_reference_to_skipper = $reference_to_skipper;
or even:
my $third_reference_skipper = \@skipper;
All three references are completely interchangeable. You can even say they're identical:
if ($reference_to_skipper = = $second_reference_to_skipper) {print "They are identical references.\n";}
This equality compares the numeric forms of the two references.
The numeric form of the reference is the unique memory address of the @skipper
internal data structure, unchanging during the life of the variable. If you
look at the string form instead, with eq or print,
you get a debugging string:
ARRAY(0x1a2b3c)
which again is unique for this array because it includes the hexadecimal (base 16) representation of the array's unique memory address. The debugging string also notes that this is an array reference. Of course, if you ever see something like this in your output, it almost certainly means there's a bug; users of your program have little interest in hex dumps of storage addresses!
Because a reference can be copied, and passing an argument to a subroutine is really just copying, you can use this code to pass a reference to the array into the subroutine:
my @skipper = qw(blue_shirt hat jacket preserver sunscreen);check_required_items("The Skipper", \@skipper);sub check_required_items {my $who = shift;my $items = shift;my @required = qw(preserver sunscreen water_bottle jacket);...}
Now $items in the subroutine will
be a reference to the array of @skipper. But how
do you get from a reference back into the original array? By dereferencing
the reference.
If you look at @skipper, you'll see
that it consists of two parts: the @ symbol and
the name of the array. Similarly, the syntax $skipper[1]
consists of the name of the array in the middle and some syntax around the outside
to get at the second element of the array (index value 1 is the second element
because you start counting index values at 0).
Here's the trick: any reference to an array can be placed in curly
braces and written in place of the name of an array, ending up with a method
to access the original array. That is, wherever you write skipper
to name the array, you use the reference inside curly braces: {
$items }. For example, both of these lines refer to the entire array:
@ skipper@{ $items }
whereas both of these refer to the second item of the array:[3]
$ skipper [1]${ $items }[1]
By using the reference form, you've decoupled the code and the method of array access from the actual array. Let's see how that changes the rest of this subroutine:
sub check_required_items {my $who = shift;my $items = shift;my @required = qw(preserver sunscreen water_bottle jacket);for my $item (@required) {unless (grep $item eq $_, @{$items}) { # not found in list?print "$who is missing $item.\n";}}}
All you did was replace @_ (the copy
of the provisions list) with @{$items}, a dereferencing
of the reference to the original provisions array. Now you can call the subroutine
a few times as before:
my @skipper = qw(blue_shirt hat jacket preserver sunscreen);check_required_items("The Skipper", \@skipper);my @professor = qw(sunscreen water_bottle slide_rule batteries radio);check_required_items("Professor", \@professor);my @gilligan = qw(red_shirt hat lucky_socks water_bottle);check_required_items("Gilligan", \@gilligan);
In each case, $items points to a
different array, so the same code applies to different arrays each time it is
invoked. This is one of the most important uses of references: decoupling the
code from the data structure on which it operates so the code can be reused
more readily.
Passing the array by reference fixes the first of the two problems
mentioned earlier. Now, instead of copying the entire provision list into the
@_ array, you get a single element of a reference
to that provisions array.
Could you have eliminated the two shifts
at the beginning of the subroutine? Sure, at the expense of clarity:
sub check_required_items {my @required = qw(preserver sunscreen water_bottle jacket);for my $item (@required) {unless (grep $item eq $_, @{$_[1]}) { # not found in list?print "$_[0] is missing $item.\n";}}}
You still have two elements in @_.
The first element is the passenger or crew member name and is used in the error
message. The second element is a reference to the correct provisions array,
used in the grep expression.
| home / programming / perl / learning / chap3 / 1 | [previous][next] |
Created: March 27, 2003
Revised: July 15, 2003
URL: http://webreference.com/programming/perl/learning/chap3/1