Professional Perl | 11
[next] |
Professional Perl
Passing Parameters
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 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|$/$||;
}
}
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.
Contents |
[next] |
Revised: March 22, 2001
Created: March 22, 2001