Passing Parameters - Page 9
July 13, 2001
Basic Perl subroutines do not have any formal way of defining
their arguments. We say 'basic' because we can optionally define
a prototype that allows us to define the types of the arguments
passed, if not their names inside the subroutine. However,
ignoring prototypes for the moment, we may pass any number of
parameters to a subroutine:
mysubroutine ("parameter1", "parameter2", 3, 4, @listparameter);
It is helpful to think of the parentheses as a conventional list
definition being passed to mysubroutine as a single
list parameter remove mysubroutine from the
above statement and what we are left with is a list. This is not
far from the truth, if we recall that declaring a subroutine
prior to using it allows us to use it as if it were a built-in
list operator. Consequently, arrays and hashes passed as
arguments to subroutines are flattened into one list internally,
just as they are when combined into a larger list.
The parameters that are passed into a subroutine appear inside
the subroutine as a list contained in the special variable
@_. This variable is made local to each subroutine,
just as $_ is inside nested foreach
loops. The definition of @_ is thus unique to each
subroutine, despite the fact that @_ is a package
variable.
One simple and common way to extract parameters passed to a
subroutine is simply to assign @_ to a list of
scalar variables, like so:
sub volume {
($height, $width, $length) = @_;
return $height * $width * $length;
}
This gives us named scalar variables we can write code for more
legibly, and also takes care of any aliasing problems that might
otherwise occur (as we will see in a moment). Alternatively, we
can use shift to pull values off the array one by
one:
sub volume {
$height = shift;
$width = shift;
$length = shift;
return $height * $width * $length;
}
This differs from the previous example in that it actually
modifies @_, removing passed parameters from the
front of the list. After all the shifts have been processed
@_ may be empty or it may contain further passed
parameters. We can use that to our advantage to write subroutines
that only use some parameters and pass the rest on. For example,
here is a speculative object method that is a wrapper for the
volume function:
sub volume {
my $self = shift;
#remove the object passed as the first parameter
return Functions::volume(@_);
#pass remaining parameters on
}
If it's brevity we are after, we can avoid assigning the contents
of @_ to anything, and simply use the values of
@_ directly. This version of volume is
not as clear as the first, but makes up for it by being only one
line long. As a result the workings of the subroutine are still
fairly obvious:
sub volume {
return $_[0] * $_[1] * $_[2];
}
The @_ array is a local array defined when the
subroutine is first entered. However, while the array is local,
the values of @_ are aliases for the original
parameters that were passed in to the subroutine. This means
that, if the parameter was a variable, modifying the values in
the @_ array modifies the original variable as well.
Used unwisely this can be an excellent way to create hard-to-
understand and difficult-to-maintain code, but if the purpose of
a subroutine is to manipulate a list of values in a consistent
and generic way, it can be surprisingly useful. Here is an
example of such a subroutine that emulates the chomp
function:
#strip the line separator '$/'
#from the end of each passed string:
sub mychomp {
foreach (@_) {
s|$/$||
;
}
}
[Lines 4 and 5 above are one line. They have been split for formatting purposes.]
This also happens to be a good demonstration of aliasing. The
subroutine actually aliases twice over; once to alias the
variables $string and @lines in the
@_ array inside the subroutine, and again in the
foreach loop that aliases the loop variable
$_ to the values in the @_ array one by
one.
We can call this subroutine in the same way as the real chomp:
mychomp $string;
mychomp @lines;
Modifying the passed arguments implies that they are modifiable
in the first place. Passing a literal value rather than a
variable will produce a syntax error. For example:
mychomp "you can't touch this \n";
This produces:
Modification of a read-only value attempted at ...
When we come to discuss prototypes we will see how we can define
subroutines that can be checked for correct usage at compile
time. This means we can create a subroutine like
mychomp that will produce a syntax error if used on
a literal variable at compile time, just like the real
chomp.
Checking for Subroutines and Defining Subroutines On the Fly - Page 8
Professional Perl Programming
Passing Lists and Hashes - Page 10
|