next up previous contents
Next: Appendix Up: Programming in Perl Previous: Writing functions   Contents

Sorting

An extremely useful builtin function in Perl is sort, which sorts an array. The default behaviour of sort is to use alphabetic sort. So, for instance, to read a list of phone numbers where each line is of the form name:phone number and print them out in alphabetical order we could write:

  while ($line = <PHONE>){
    ($name,$phone) = split /:/,$line;   
    $phonehash{$name} = $phone;       # Store phone number in a hash
  }

  foreach $k (sort keys %phonehash){
     print $k, ":", $phonehash{$k},"\n");
  }

Here, we sort the list generated by keys %phonehash before printout out the values in the hash.

What if we want to supply a different basis for comparing values? We can supply sort with the name of a comparison function. Then comparison function we supply will be invoked by sort. Instead of using @_ to pass parameters to the comparison function (as with normal functions), sort will pass the values as $a and $b. The comparison function should return $-1$ if the first argument, $a, is smaller than the second argument, $b$, 0 if the two arguments are equal, and 1 if the second argument is smaller than the first. So, we can sort using a numeric comparison function as follows.

   foreach $k (sort bynumber keys %somehash){
     ...
   }

   sub bynumber {
     if ($a < $b) {return -1};
     if ($a > $b) {return 1};
     return 0;
   }

In fact, this is so common that Perl supplies a builtin ``spaceship'' operator <=> that has this effect.

   sub bynumber {
     return $a <=> $b;  # Return -1 if $a < $b,
                        #        +1 if $a > $b,
                        #         0 if $a == $b
   }

The operator cmp achieves the same effect as <=> but using string comparison instead of numeric comparison.

To sort in descending order, we simply invert the position of the arguments in the comparison function.

   foreach $k (sort bynumdesc keys %somehash){
     ...
   }

   sub bynumberdesc {return $b <=> $a;}

We can also use the arguments $a and $b in more complex ways. For instance, to print out a hash based on the numeric sorted order of its values, we can write:

   foreach $k (sort bystrvalue keys %somehash){
     ...
   }

   sub bystrvalue {return $somehash{$a} cmp $somehash{$b};}

Finally, we can avoid using a separate named comparison function by just supplying the expression that is to be evaluated in braces, as follows:

   foreach $k (sort {$a <=> $b} keys %somehash){  # Same as bynumber
   foreach $k (sort {$b <=> $a} keys %somehash){  # Same as bynumdesc
   foreach $k (sort {$somehash{$a} cmp $somehash{$b}} 
                      keys %somehash){            # Same as bystrvalue


next up previous contents
Next: Appendix Up: Programming in Perl Previous: Writing functions   Contents
Madhavan Mukund 2004-04-29