Converting Scalar Subroutines into List Processors - Page 11
July 13, 2001
Consider this subroutine, which capitalizes the first letter of
the string that it is passed:
sub capitalize {
$_[0] = ucfirst(lc $_[0]);
print "$_[0]";
}
$country = "england";
capitalize($country); # produces 'England'
Simple enough, but it only works on one string at a time.
However, just because we wrote this subroutine to work as a
scalar operator does not alter the fact that in reality it is
working on a list. We have just limited it to handle a list with
one value. With only a little extra effort we can turn this
subroutine into something that works on scalars and lists alike:
sub capitalize {
foreach (@_) {
$_ = ucfirst lc;
# lc uses $_ if argument is omitted
print "$_[0]";
}
}
Or more efficiently, with map:
sub capitalize {
map {$_ = ucfirst lc} @_;
print "$_[0]";
}
This version works identically for calls like the above that pass
only one parameter, but also happily works on arrays too:
sub capitalize {
map {$_ = ucfirst lc} @_;
print "@_[0, 1, 2]";
}
@countries = ("england", "scotland", "wales");
capitalize (@countries);
# produces ("England", "Scotland", "Wales")
Passing '@_' Directly into Subroutines
We said earlier that the @_ array is distinct to
each subroutine and masks any previous definition. That is almost
true there is one exception provided, for reasons of
efficiency, to the Perl programmers dedicated to optimizing their
code. Normally @_ is defined locally, on entry to
each subroutine. So, if we pass in no parameters at all we get an
empty array. However, if we call a subroutine using the
& prefix and do not pass parameters or use
braces then the subroutine inherits the @_ array of
the calling subroutine directly:
&mysubroutine; #inherit @_ from parent
The problem with this technique is that it is rather arcane, and
not obvious to the reader of our code. Therefore, if we use it, a
comment to the effect that this is what we are doing (such as the
one above) is highly recommended.
As far as the subroutine is concerned this is no different to
passing the @_ array as a parameter:
mysubroutine(@_);
Although, this may seem equivalent, in the second case the
@_ array is copied each time the call is made. If
@_ contains a large number of values, or many calls
are made (for instance in a recursive subroutine) then this is
potentially expensive. The &mysubroutine;
notation passes the @_ array directly, without
making a copy, and so avoids the unnecessary work. Whether this
is worth the trouble or not is of course another matter. If
@_ only contains a few elements, it is probably
better to live with the very minor inefficiency of copying the
array and use the explicit version.
Note that the aliasing of the values in the @_ array to the
original variables (if the parameter was a variable) happens in
either case, so it is not necessary to resort to this practice if
all we want to do is modify the variables that were passed to us.
Passing Lists and Hashes - Page 10
Professional Perl Programming
Named Parameters - Page 12
|