You want to sort a list of numbers, but Perl’s
sort
(by default) sorts alphabetically in ASCII
order.
Use Perl’s
sort
function and
the <=> numerical comparison operator:
@sorted = sort { $a <=> $b } @unsorted;
The sort
function takes an optional code block,
which lets you replace the default alphabetic comparison subroutine
with your own. This comparison function is called each time
sort
has to compare two values. The values to
compare are loaded into the special package variables
$a
and $b
, which are
automatically local
ized.
The comparison function should return a negative number if
$a
ought to appear before $b
in
the output list, 0
if they’re the same and
their order doesn’t matter, or a positive number if
$a
ought to appear after $b
.
Perl has two operators that behave this way: <=> for sorting
numbers in ascending numeric order, and
cmp
for
sorting strings in ascending alphabetic order. By default,
sort
uses cmp
-style
comparisons.
Here’s code that sorts the list of PIDs in
@pids
, lets the user select one, then sends it a
TERM signal followed by a KILL signal. We use a code block that
compares $a
to $b
with
<=> to sort numerically:
# @pids is an unsorted array of process IDs foreach my $pid (sort { $a <=> $b } @pids) { print "$pid\n"; } print "Select a process ID to kill:\n"; chomp ($pid = <>); die "Exiting ... \n" unless $pid && $pid =~ /^\d+$/; kill('TERM',$pid); sleep 2; kill('KILL',$pid);
If you use $a
<=>
$b
or $a
cmp
$b
, the list will be sorted in ascending order.
For a descending sort, all we have to do is swap
$a
and $b
in the sort
subroutine:
@descending = sort { $b <=> $a } @unsorted;
Comparison routines must be consistent; that is, they should always return the same answer when called with the same values. Inconsistent comparison routines lead to infinite loops or core dumps, especially in older releases of Perl.
You can also say sort
SUBNAME
LIST
where SUBNAME
is the name
of a comparison subroutine returning -1
,
0
, or +1
. In the interests of
speed, the normal calling conventions are bypassed, and the values to
be compared magically appear for the duration of the subroutine in
the global package variables $a
and
$b
. Because of the odd way Perl calls this
subroutine, it may not be recursive.
A word of warning: $a
and $b
are set in the package active in the call to sort
,
which may not be the same as the one that the
SUBNAME
function passed to sort
was compiled in! For example:
package Sort_Subs; sub revnum { $b <=> $a } package Other_Pack; @all = sort Sort_Subs::revnum 4, 19, 8, 3;
This will silently fail (unless you have -w in effect, in which case it will vocally
fail), because the sort
call sets the package
variables $a
and $b
in its own
package, Other_Pack, but the revnum
function uses
its own package’s versions. This is another reason why
in-lining sort functions is easier, as in:
@all = sort { $b <=> $a } 4, 19, 8, 3;
For more on packages, see Chapter 10.
The cmp
and <=> operators in
perlop(1) and Chapter 2 of
Programming Perl; the
kill
, sort
, and
sleep
functions in perlfunc
(1) and Chapter 3 of Programming Perl
; Section 4.15
Get Perl Cookbook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.