Back  Index  Next

Subroutines and Sorting

Subroutines (a.k.a. procedures or functions or methods) are (as you might expect by now) rather simple and straightforward in Perl.

Declaring a new subroutine


sub hello {
    print "hello how are you?\n";
}
The name can be any valid Perl identifier. Note that the indentation style is similar to that of the 'if' or 'while'.

Calling a Subroutine


hello;        # prints "hello how are you?"
Subroutines are very useful for breaking up a long program up into manageable pieces. Giving a sequence of lines a meaningful name will also add to the clarity of your program.

init;
process;
show_results;
clean_up;
If you declare the subroutine BELOW the place where you call it you need to either precede the name with an ampersand '&' or use parentheses:

&bye;       # or bye()

sub bye {
    print "adios!\n";
}

Input parameters

You can pass input parameters to the subroutine in the same way that you pass parameters to the Perl built-in functions like 'substr' or 'index'. The parameters get magically assigned to a special array very cryptically named @_. Bizzare, huh? The first element of the array @_ is referenced (as usual) with $_[0]. Looks odd, huh?

sub hello {
    print "Well done $_[0]!  You did it $_[1] times!\n";
}

hello("Joe", 3);        # Well done Joe!  You did it 3 times!
Alternatively:

sub hello {
    ($name, $n) = @_;
    print "Well done $name!  You did it $n times!\n";
}
The above is the normal and recommended way to handle parameters. It gets copies of the parameters and names them.

Local variables

An even better way to handle input parameters is to copy them to variables that are entirely private to the subroutine:

sub hello {
    my ($name, $n) = @_;
    ...
}
With the 'my' keyword the variables $x and $y can be seen ONLY within the scope of the subroutine. This prevents the subroutine from clobbering any similarily named variables outside of itself. This can easily happen - especially when a program grows in size and you tend to use the same variable name a lot - such as $i.

NOTE - be sure to put the 'my' variables within parentheses. This makes the assignment happen in list (or array) context.

You can often (but not always) omit the parentheses on a subroutine call because the semi-colon ';' will tell Perl where the parameters end.

How many parameters?

What if you are passing a variable number of parameters? How do you know how many you are receiving? Well, they are put in the array @_. What is the size of that array? As usual you can get the size of an array by assigning @_ to a scalar or by putting it in scalar context.

sub show {
    my $n = @_;
    if ($n < 2) {
        die "need two or more parameters for 'show'\n";
    }
    my ($name, $age) = @_;
    ...
}
More cryptically you could have done this:

@_ >= 2 or die "need two or more parameters for 'show'!\n";
Ponder on that one! Sometimes what at first appears overly concise and cryptic can eventually become very clear and clean.

Returning a Value

Subroutines often want to return a value to the caller in the same way that the 'index' built-in function does. It makes for a clean and clear syntax. Here are some fairly complex examples. Study them carefully.

sub discrim {
    my ($a, $b, $c) = @_;
    return $b**2 - 4*$a*$c;
}

sub confirm {
    my ($question) = @_;
    print "$question ";
    my ($ans) = <STDIN>;
    chomp $ans;
    my ($first) = substr($ans, 0, 1);
    if ($first eq "y" or $first eq "Y") {
        return 1;        # true
    } else {
        return 0;        # false
    }
}

print discr(1, 3, 4), "\n";

if (confirm("Are you sure?")) {
    print "Okay, I'll do it!\n";
}

Sorting Arrays

Sorting arrays is done with the keyword 'sort'.

@names  = ("Joe", "Paul", "gretchen", "Alley", "Bunny");

@snames = sort @names;
print "@snames\n";    # Alley Bunny Joe Paul gretchen
This sorts the array alphabetically (or ASCIIbetically). Capital letters come before lower case. This may not always be what you want. For example it will not work for sorting a series of numbers.

To sort in a different order you must supply the sort function with a different comparison routine.


@nums = (9, 3, 4, 10, 1);

sub numerically {       # whatever name you wish
    if ($a < $b) {
        return -1;
    } elsif ($a == $b) {
        return 0;
    } else {
        return 1;
    }
}
@snums = sort numerically @nums;
print "@snums\n";    # 1 3 4 9 10
Whenever 'sort' needs to compare two elements of the array it will call the subroutine 'numerically'. The elements are 'magically' passed by way of the names $a and $b. It is the job of the sort subroutine to look carefully at $a and $b and return -1, 0 or 1. It will:

    return-1if $a isless than$b
    return0if $a isequal to$b
    return1if $a isgreater than$b

You can see the process of sorting in action by inserting a print in the sort subroutine:


sub numerically {
    print "We are comparing $a and $b\n";
    ...
}
'numerically' will be called many many times. This is a different way of using a subroutine. You do not call it yourself. It is called for you by the sort function whenever it needs to compare two numbers.

Exercises

  1. Make a subroutine to return the maximum value of its two parameters.

    
    print max(4, 7);        # 7
    print max(4, 1);        # 4
    
    Extend your subroutine to allow any number of values as input.
    
    print max(1, 3, 2, 61, 2);      # 61
    
  2. Read a series of words from a file and print them sorted by the length of the word. This input:

    
    Harry
    Penelope
    Joe
    Paul
    
    results in this output:
    
    Joe
    Paul
    Harry
    Penelope
    

Back  Index  Next