Parameters (Continued) - Page 15
July 13, 2001
We can also use @ and % in prototype
definitions, and it is sometimes helpful to consider subroutines
without prototypes as having a default prototype of
(@); that is:
sub mysubroutine (@) {...}
Just like unprototyped subroutines, the single @
prototype will absorb all values, flattening any lists or hashes
it finds. It follows from this that a prototype of
(@,@) is just as invalid as it was
before. However, if we want to enforce an array variable, as
opposed to a mere list, that's a different story, as we will see
shortly.
A @ or % prototype matches all
parameters in the argument list from the point it is defined to
the end of the list. Indeed, % and @ are actually identical in
meaning to Perl, since passing a hash turns it into a list.
Recall that there is no such thing as 'hash context'. It cannot
check that passed parameters came from a hash due to flattening,
nor that the remaining parameters divide evenly into pairs
because that is a run-time issue. However, this does not mean
they are of no use. It means that the only useful place for
either prototype character is at the end of the prototype. As an
example, here is a subroutine, which joins array elements
incorporating a prefix and suffix. It takes a minimum of three
parameters, but has no maximum because of the @
prototype:
#!/usr/bin/perl
# join.pl
use warnings;
sub wrapjoin ($$$@) {
my ($join, $left, $right, @strings) = @_;
foreach (@strings) {
$_ = $left. $_. $right;
}
return join $join, @strings;
}
print wrapjoin("\n", "[","]", "One", "Two", "Three");
Without the @ we could only pass three arguments. If
we added more $ characters we could allow more, but
then we would be forced to supply that many arguments. The
@ allows an arbitrary number, so long as we also
supply three scalars to satisfy the initial $$$.
Lists can validly be empty, so the prototype does not ensure that
we actually get passed something to join. We could attempt to fix
that by requiring a fourth scalar, like this:
sub wrapjoin ($$$$@) {
($join, $left, $right, @strings) = @_;
}
However, a little thought reveals a flaw in this design. A
literal list of strings works fine, but if the caller supplies an
actual array variable for the fourth argument it gets converted
to a scalar. In effect, we have introduced a new bug by adding
the prototype.
The moral here is that prototypes can be tricky and can even
introduce bugs. They are not a universal band-aid for fixing
subroutine calling problems. If we want to detect and flag an
error for an empty list, prototypes cannot help us — we
will have to write the subroutine to handle it explicitly at run
time.
Prototypes - Page 14
Professional Perl Programming
Prototyping Code References - Page 16
|