Take my Parameters, Please
March 13, 2000
Another great use for references is passing data between
subroutines. In theory, most well-designed Perl programs will
contain subroutines to perform repetitive or specialized tasks.
Let's say that you have a subroutine &summary,
which takes two parameters: a label (scalar) and a list of
surnames. The subroutine creates some sort of chart of these
names using the label as a header -- the trick here is that you
need to pass one scalar and one list to the subroutine. Your
instinct might be to construct a call such as:
print &summary($label,@surnames);
But this would be wrong, so very wrong. Why? Because a subroutine
accepts parameters as a flat list into the built-in list variable
@_; that means the above call would simply pass the value of
$label followed by each of the items in @surnames
as one long list. Whereas, your subroutine will want to receive
only two values; the first being a scalar, the second being one
list.
Instead, you want to pass a reference to the list; because the
reference is a scalar, it travels lightly into the subroutine,
which can then dereference it into a list. Sound confusing?
Here's the call:
print &summary($label,\@surnames);
And here is the beginning of the subroutine:
sub summary {
my ($label,$surnameRef)=@_;
my @surnames=@{$surnameRef};
print "Title: $label\n";
print join (",",@surnames);
etc...
}
An important point to remember here occurs in line two above;
two local variables receive the parameters from the incoming
list @_. The second parameter is a scalar because it is
receiving a reference; therefore, $surnameRef itself
becomes a reference to the array referenced by \@surnames
in the calling statement. To use this list in list context, we
need to dereference, which here is done by dereferencing
$surnameRef into a normal list variable @surnames.
We could have dereferenced on-the-fly in the join()
function, as follows, which is more efficient but slightly more
obtuse to read:
print join (",",@{$surnameRef});
When passing data between subroutines, you can think of
references as an effective way to package or cloak a complex
data structure and pass it wholly into a subroutine, where it
can be unpackaged -- or dereferenced -- into its data structure,
be it a list or a hash. Of course, keep in mind that the
reference only points to data, which means if the data pointed
to changes, the data you get by dereferencing changes.
Taking this example one step further, imagine that you want to
pass a particular hash into a subroutine, and that subroutine
will return a different hash.
%grades=("Tom Jones"=>92,"Briget
Gidget"=>85);
$label="Grade Summary";
print &summary($label,\%grades)->{"header"};
print "<TABLE>".&summary($label,\%grades)->{"body"}."</TABLE>";
sub summary {
my ($label,$grades)=@_;
my %tables=("header"=>"","body"=>"");
$tables{"header"}="<H2>$label</H2>";
foreach $key (keys %{$grades}) {
$tables{"body"}.="<TR><TD>$key</TD><TD>$grades->{$key}</TD></TR>";
}
return \%tables;
}
Admittedly, this final example may take a second read or two.
Or three. The goal of this program will be to output HTML code
of a table that summarizes the grades contained in the
%grades hash. First, we create a typical hash, and a
scalar containing a label. Now we get tricky. The first print
statement calls the &summary subroutine, passing it
$label and a reference to the %grades hash.
Ultimately, the subroutine returns a reference to a hash of
results, one key of which is "header". So, think of the
whole subroutine call as one big hash reference. As such, we
dereference the subroutine call to retrieve the value of the
"header" key.
The same concept applies to the second print statement. Again,
the entire &summary subroutine is treated as a hash
reference, because that is what it returns. And so we dereference
the "body" key.
Turning to the subroutine itself, it accepts the two parameters
into a local scalar and a local reference, $grades. The
local hash %tables is built to contain the results, and
the value for the "header" key is assigned. A tricky
foreach loop dereferences $grades to pull out its
keys, and iterates over each key. For each key, an HTML table
row is constructed using the key and the key's value,
dereferenced again from $grades. And that's just two
lines of code!
The HTML that is built is then concatenated into the value of
the "body" key for our local %tables hash.
Finally, the subroutine returns a reference to this hash --
that is what lets us treat this subroutine as if it is one big
hash reference, as seen in the print statement.
Shall we dance, sweet reference?
The Perl You Need to Know
Come Sail Away
|