EIW Fall 2003 Lecture Notes

EIW Lecture Notes - Perl Subroutines


Subroutines and sub

Subroutines are essential when writing complex programs, we won't go over all the benefits of subroutines, or spend much time studying the difference between private (local) or global variables, or even discuss the general concept of a subroutine - it is expected that you've already covered these topics with some other computer language

Perl allows you to create subroutines using the sub control structure. Once you create a subroutine you can call it many times, just as if it were any of the built-in perl functions (for example: print, split, sort). Here is the general form of the sub command:

sub subname {
   statement1;
   statement2;
   # you can put more statements here
}

subname is the name of the subroutine to be created. "C" programmers may notice that there is no place to specify the arguments to the subroutine! When defining a subroutine in perl you don't specify how many arguments there are (or give them formal names), instead there is a simple standard interface to all subroutines that perl uses to provide the subroutine with a list of values that were supplied when the subroutine is called. Perl subroutines always get a list of arguments in the default perl array variable named @_.

A "C" programmer might also notice the lack of anything that specifies what kind of value is returned by the subroutine (if any). In perl, a subroutine can return any kind of value (scalar, array, or associative array).

Here is an example of a perl subroutine that simply returns the string "Hi Dave":

   sub foo {   
     "Hi Dave";  
   }

Aside from the obvious (that this subroutine is pretty useless), you might notice that there is no return statement. In perl subroutines you can use a return statement if you want to, but you don't have to. If you don't use a return statement, the return value of the subroutine is the value of the last statement executed (in this case the string constant "Hi Dave").

Calling (invocation) of a subroutine

Perl subroutines can be called within any perl expression (or as an complete statement) by preceding the subroutine name with the "&" character. If a list of parameters is included in the subroutine call it can be within parentheses (so it looks like a "C" function call), but the parameters don't need to be in parentheses. The following are all valid calls of the foo procedure defined above:

&foo;
&foo();
&foo("Hi Dave",22);
&foo "By Dave";

Although it doesn't make sense to pass parameters to our foo subroutine (since it doesn't do anything with them), it doesn't hurt us like it would in "C" (no error occurs).

Access to global variables

Any variable that is created outside of a subroutine (in the perl main program) is a global variable, so it can also be accessed inside your subroutines. We've already mentioned one important variable - the default perl array variable @_. Before we consider how to extract parameters passed to a subroutine through @_, here is an example that shows that you can access global variables inside a subroutine.

# perl program to calculate degrees celsius using a subroutine.
# ask the user for a temperature in degrees fahr.

print "Enter degrees fahrenheit:\n";

# read in the value from stdin (and chop off the newline)
chop($degf = <>);

# call the subroutine printcelsius

printcelsius();


# ==========================================
# here we define the subroutine printcelsius

sub printcelsius {
  $degc = ($degf - 32) * (5/9);
  print "$degf degrees fahrenheit is $degc degrees celsius\n";
}

You can see that the variable $degf can be accessed inside the subroutine (we could also change it's value in the subroutine). Another thing to notice is that the subroutine definition can go anywhere in the perl program. It is usually good idea to put subroutine definitions together and away from the main program, but perl doesn't care where they are. The following works fine:


# perl program to calculate degrees celsius using a subroutine.
# ask the user for a temperature in degrees fahr.

print "Enter degrees fahrenheit:\n";

# ==========================================
# here we define the subroutine printcelsius

sub printcelsius {
  $degc = ($degf - 32) * (5/9);
  print "$degf degrees fahrenheit is $degc degrees celsius\n";
}

# read in the value from stdin (and chop off the newline)
chop($degf = <>);

# call the subroutine printcelsius

printcelsius();


Getting at parameters

Whenever you call a subroutine, the perl interpreter creates a list of any parameters passed to the subroutine and stores this list in a special version of the variable @_ that is usable only by the called subroutine. Once the subroutine returns the main program still has it's @_ array intact (the subroutine gets it's own @_ that is different than the @_ used by the main program or calling subroutine).

Although you can deal directly with the @_ array in your subroutine, it is typical to immediately transfer the parameters to named variables that are private to the subroutine. By private, we mean that these variables are new variables and are not to be confused with any global variables that might have the same name.

Here is what a typical perl subroutine that uses parameters looks like:

# define a subroutine that returns the smaller of 2 numbers

sub min {
   my($a,$b) = @_;        # $a and $b are private variables
                          # and get values from the array @_
          
   if ($a < $b) {
      $a;
   } else {
      $b;
   }
}

# here is the main program - it calls min

$b = 100;
print "The smaller number is " , &min(10,11), "\n";
# here we print b - it's always 100
print "b is $b\n";

The line my($a,$b) = @_; is shorthand for the following:

my($a,$b);        # declare $a and $b to be private to this subroutine
($a,$b) = @_;     # $a is $_[0] and $b is $_[1]

You should also notice that there are no return statements in the subroutine, so the subroutine returns the value of the last expression executed - in this case either $a or $b. You can use return statements if you want - it would look like this:

# define a subroutine that returns the smaller of 2 numbers

sub min {
   my($a,$b) = @_;     # $a and $b are private variables
                          # and get values from the array @_
          
   if ($a < $b) {
     return $a;           # don't need parentheses
   } else {
     return($b);          # but you can use parentheses
   }
}

# here is the main program - it calls min

$b = 100;
print "The smaller number is " , &min(10,11), "\n";
# here we print b - it's always 100
print "b is $b\n";

Since $a and $b are declared as private, any value assigned to them is lost when the subroutine is done executing. It also means that within the subroutine the variable $a does not refer to any global variable with the name $a.

The point of this code is that the global variable $b is completely different than the $b inside the subroutine - they are independent of each other. The value of $b that is printed out is always 100.

Passing an Array

You can pass an entire array to a subroutine, here is an example that includes a subroutine that will add up all the values held in an array and return the sum.

# a subroutine that sums the elements in an array
# and returns the sum

sub sum_array {
    my(@vals) = @_;        # put parameters in array @vals
    my($sum) = 0;       # initialize the sum to 0

    # $sum is private so we don't need to worry about
    # clobbering any global variable named $sum!

    foreach $i (@vals) {
	$sum = $sum + $i;
    }
    return($sum);
}

# main program

print "Enter a bunch of numbers on a line\n";

$_ = ;     

@nums = split;

print "The sum is ", &sum_array(@nums), "\n";

The prefix &

When calling a subroutine you can add the prefix "&" to the name of the subroutine. You don't have to! Lots of perl code that you might come across uses the & (typically older code), although you don't need it in most situations. (If you are really curious about the situations in which you might need it - check the latest edition of "Learning Perl" by Wall, Christiansen and Schwartz for an explanation).