HOME  |    TRAINING  |   FREE TUTORIALS   |   JOBS
Find out more about our new RSS feed.
FREE Tutorial
PROFESSIONAL PERL PART 2 - PASSING PARAMETERS

CATEGORY
SEARCH OUR OTHER TUTORIALS

DESCRIPTION

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.
Click here to be kept informed of our new Tutorials.


This free tutorial is a sample from the book Professional Perl Programming.


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.

Passing Lists and Hashes

We mentioned earlier, when we started on the subject of passed arguments, that passing lists and hashes directly into a subroutine causes list flattening to occur, just as it does with ordinary list definitions. Consequently, if we want to pass an array or hash to a subroutine, and keep it intact and separate from the other arguments, we need to take additional steps. Consider the following snippet of code:

$message = "Testing";
@count = (1, 2, 3);
testing ($message, @count);  # calls 'testing' -- see below

The array @count is flattened with $message in the @_ array created as a result of this subroutine, so as far as the subroutine is concerned the following call is actually identical:

testing ("Testing", 1, 2, 3);

In many cases this is exactly what we need. To read the subroutine parameters we can just extract the first scalar variable as the message and put everything else into the count:

sub testing {
($message, @count) = @_;
...
}

Or, using shift:

sub testing {
$message = shift;
# now we can use @_ directly in place of @count
...
}

The same principle works for hashes, which as far as the subroutine is concerned is just more values. It is up to the subroutine to pick up the contents of @_ and convert them back into a hash: "for sorrow", 2 => "for joy", 3 => "for health",4 => "for wealth", 5 => "for sickness", 6 => "for death");

However, this only works because the last parameter we extract inside the subroutine absorbs all the remaining passed parameters. If we were to write the subroutine to pass the list first and then the scalar afterwards, all the parameters are absorbed into the list and the scalar is left undefined:

sub testing {
(@count, $message) = @_;  # ERROR
print "@_";
} 

testing(1, 2, 3, "Testing");
# results in @count = (1, 2, 3, "Testing") and $message = undef

If we can define all our subroutines like this we won't have anything to worry about, but if we want to pass more than one list we still have a problem.

If we attempt to pass both lists as-is, then extract them inside the subroutine, we end up with both lists into the first and the second left undefined:

sub testing {
my (@messages, @count) = @_; # wrong!
print "@_";
}

@msgs = ("Testing", "Testing");
@count = (1, 2, 3);
testing(@msgs, @count);
# results in @messages = ("Testing", "Testing", "Testing", 1, 2, 3) and
# @count = ();

The correct way to pass lists and hashes, and keep them intact and separate, is to pass references. Since a reference is a scalar, it is not flattened like the original value and so our data remains intact in the form that we originally supplied it:

testing (["Testing", "Testing"], [1, 2, 3]);  # with two lists
testing (\@messages, \@count);  # with two array variables
testing ($aref1, $aref2);  # with two list references

Inside the subroutine we then extract the two list references into scalar variables and dereference them using either @{$aref} or $aref->[index] to access the list values:

sub testing {
($messages, $count) = @_;
# print the testing messages
  foreach (@ {$messages}) {
  print "$_ ... ";
}
print "\n";
# print the count; 
foreach (@ {$count}) {
  print "$_! \n";
}
}

Another benefit of this technique is efficiency; it is better to pass two scalar variables (the references) than it is to pass the original lists. The lists may contain values that are large both in size and number. Since Perl must store a local copy of the @_ array for every new subroutine call in the calling stack, passing references instead of large lists can save Perl a lot of time and memory.

Continued...


NEXT PAGE



5 RELATED COURSES AVAILABLE
MICROSOFT VISUAL BASIC V6 INTRODUCTION
To go from the fundamentals of Visual Basic programming to the threshold of Advanced level. Gaining in depth prog....
MICROSOFT VISUAL BASIC 5.0 PROFESSIONAL INTRODUCTION
To provide readers with a solid foundation upon which to build Windows applications using Visual Basic 5. Readers....
MICROSOFT VISUAL BASIC 5.0 CLIENT SERVER DEVELOPMENT
This course teaches the skills required to develop client server applications using MS Visual Basic 5.0 Enterpris....
C++ PROGRAMMING
Object oriented programming is fast becoming the leading software design methodology, with C++ becoming ever more....
C PROGRAMMING
This course is design to provide non-C programmers with the essential skills and knowledge necessary to allow the....
 
0 RELATED JOBS AVAILABLE
CONTACT US
Friday 25th July 2008  © COPYRIGHT 2008 - VISUALSOFT