Passing Values into Subroutines

When you've got local variables to store values specific to a subroutine, and return values to send data out from the subroutine, the only part of the subroutine left is getting information into it. For that you use arguments, just as you do for the built-in Perl functions.

Passing Arguments

Let's start with how arguments get passed into subroutines. Perl has an extremely loose notion of subroutine arguments. Whereas in other languages you have to be very specific when you write your subroutines to make sure you indicate how many and what type of arguments your subroutine will take, Perl doesn't care. When you call a subroutine in Perl, any and all arguments you give it are combined or expanded into a single list of values. For scalar arguments, this is no big deal:

&mysub(1,2,3);

The &mysub() subroutine in this case will get a list of three numeric values. Watch out for lists, though:

&mysub(@list, @anotherlist);

In this case, both @list and @anotherlist are expanded into their component values and combined into one list. It's that single list that ends up inside the body of the subroutine. Your original arrays lose their identities when passed into a subroutine. This behavior is identical to the behavior when you assign multiple lists to a single array, as described on Day 4, “Working with Lists and Arrays.”

A similar thing happens with hashes; the hash is expanded into its keys and values (following the usual hash rules), combined with any other list arguments, and passed into the subroutine as a flat list.

But what if you really want your arguments to consist of multiple arrays or hashes? Perl's argument-passing behavior doesn't make this easy, but there are a number of ways to work around it. One way is to store your arrays or hashes as global variables and just refer to them in the body of your subroutines. Another way is to reconstruct the arrays inside the subroutine using a clever hack (for example, including the count of the first array as an argument to the subroutine). A third way, and arguably the best one, is to pass the arguments to the subroutine as references, which retains the construction of the original arrays or hashes inside the subroutine. You'll learn about references later on in this book on Day 19, “Working with References.”

Handling Arguments Inside Subroutines

Okay, so arguments are passed into a subroutine as a single list of values. How do you then get to those arguments from inside the body of your subroutine?

The list of arguments passed to your subroutine is stored in the special local variable @_. You can access elements of that array, or split those values into individual values using the standard array access and assignment expressions. The @_ variable is a local variable to the subroutine; if you set a @_ global variable, its values are hidden inside the subroutine, and if you call another subroutine from inside a subroutine, that second subroutine will get its own version of @_.

Here's an example of a subroutine that adds together its two arguments (and a line of code showing how it's called:

&addthem(2,3);

sub addthem {
   return $_[0] + $_[1];
}

The two arguments to the subroutine—here 2 and 3—are put into the list stored in the @_ variable. Then you can just use $_[0] and $_[1] access forms to get to those values. Keep in mind that just as $foo[0] and $foo refer to different things, so are $_[0] and $_ different (the first one is the first element in the argument list; the second is the special default variable).

Note that this subroutine is kind of limited (brain-dead might be a better term)—it only adds together its first two arguments, no matter how many you give it. Because you cannot control how many arguments you can get inside a subroutine, you have to be careful that you only call the subroutine with the right number of arguments, test the number or type of arguments you get, or write the subroutine generally enough to be able to handle multiple arguments. We might modify the above subroutine to add all its arguments together, regardless of the number, like this:

sub addthem {
   my $sum = 0;
   foreach $i (@_) {
      $sum += $i;
   }
   return $sum;
}

Note

Recent versions of Perl (after 5.003) do actually provide a way to define a subroutine to only take specific numbers and types of arguments. Given that this feature is quite new, it's probably not something you want to rely on in your own scripts. More about subroutine prototypes in the “Going Deeper” section.


Perl doesn't have a way of explicitly naming incoming arguments, but one common trick is to split out the array of arguments into local variables as a first step, like this, where this subroutine expects three arguments:

sub foo {
   my($max, $min, $inc) = @_;
   ...
}

You can then refer to those arguments using mnemonic local variable names rather than having to keep track of their positions in the array all the time. Another (very common) way of doing this same thing is to use shift. Conveniently, shift with no arguments inside a subroutine will extract the first element from @_ (pop will do the same thing to the last element):

sub foo {
   my $max = shift;
   my $min = shift;
   my $inc = shift;
   ...
}

A Note on Arguments Passed by Reference

The argument list that you get inside your subroutine via @_ are implicit references to the values that were passed in from outside. That means that if you pass in a list of strings, and inside the body of your subroutine, modify those strings (by altering the contents of the @_ array itself), then the strings will remain modified outside the body of the subroutine as well. As I mentioned earlier, however, arrays and hashes do not maintain their integrity inside the subroutine, nor can you modify a number. And, if you assign the argument list @_ to a local variable, those values will all be copied and cease to be references. Because Perl's notion of pass by reference is rather vague, it's not commonly used as such; using actual references to pass in arrays or hashes of values is a much more direct way of approaching the problem.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.29.48