Requiring Variables Rather than Values - Page 17
July 13, 2001
So far we have seen how to enforce a specific number of arguments
and their scope, if not their data type. We can also use
prototypes to require that an actual variable be passed. This is
invaluable when we want to implement a subroutine that modifies
its passed parameters, such as the capitalize
example just above.
To require a variable, we again use a $,
@, and % character to specify the type,
but now we prefix it with a backslash. This does not, as it might
suggest, mean that the subroutine requires a reference to a
scalar, array, or hash variable. Instead, it causes Perl to
require a variable instead of merely a value. It also causes Perl
to automatically pass the variable as a reference:
#!/usr/bin/perl
# varproto.pl
use warnings;
use strict;
sub capitalize (\$) {
${$_[0]} = ucfirst (lc ${$_[0]});
}
my $country = "england";
capitalize $country;
print $country, "\n";
# capitalize "scotland"; # ERROR: compile-time syntax error!
If we tried to call capitalize with a literal string
value, we would get the error:
Type of arg 1 to
main::capitalize must be scalar (not constant item) at ..., near
""england";"
The fact that Perl automatically passes variables as references
is very important, because it provides a new way to avoid the
problem of list flattening. In other words, prototypes allow us
to pass arrays and hashes to a subroutine as-is, without
resorting to references in the subroutine call.
A push is an example of a built-in function that
works by taking an array as its first argument. We do not need to
treat that variable specially to avoid flattening, and we can
replicate that syntax in our own code by defining a prototype of
(\@@). The following subroutine uses the list-
processing version of capitalize to produce a
capitalizing push subroutine. First it removes the
array variable using shift, then capitalizes the
rest of the arguments and adds them to the variable with
push. Perl, being versatile, lets us do the whole
thing in one line:
sub pushcapitalize (\@@) {
push @{shift}, capitalize(@_);
}
We can use this subroutine just like we use the push
function
pushcapitalize @countries, "england";
pushcapitalize @countries, "scotland", "wales";
pushcapitalize @countries, @places; # no flattening here!
Note that we omitted the parentheses, which requires that the
subroutine be either already defined or predeclared.
Hash variables are requested using \%, which unlike
% does have a different meaning to its array counterpart
\@. Here is an example that flips a hash variable
around so that the keys become values and the values become keys.
If two keys have the same value one of them will be lost in the
transition, but for the sake of simplicity we'll ignore that
here:
sub flip (\%) {
@hash = %{$_[0]};
%{$_[0]} = reverse @hash;
}
This subroutine makes use of the fact that a hash is essentially
just a list with an even number of values, and a little extra
cleverness allows quick key access. So, to flip the hash we turn
it into a list and reverse it. This also reverses each key-value
pair with respect to each other; we then turn it back into a hash
again.
Although Perl will automatically pass variables as references
when a variable prototype is in effect, it will allow an explicit
reference if we dereference it first. The two following calls are
both valid uses of the above subroutines:
For the pushcapitalize subroutine:
pushcapitalize @{$countries_ref}, "england";
And for the flip subroutine:
flip %{$hash_ref};
Before we finish with variable prototypes it is worth mentioning,
just for completeness, that \& also has a
meaning subtly different from &. It requires
that the passed code reference be a reference to an actual
subroutine, that is, a code reference defined using
$coderef = sub {...} or $coderef =
\&mysubroutine. A reference to an in line bare block
(such as in mysub {...} @list) will not be accepted.
Another way to look at \ is that it requires that
the argument actually starts with the character it precedes:
\& therefore means that the argument must start
&, not {.
Prototyping Code References - Page 16
Professional Perl Programming
Optional Parameters - Page 18
|