The sortsub (the stuff in {}
after the sort
) defines a two-tier sort: first by name, then by number of legs. The or
implements the cross-over between the two criteria. It's easier to see if you format the code differently:
@animals = sort {
$animals{$a}{'name'} cmp $animals{$b}{'name'} or
$animals{$a}{'legs'} <=> $animals{$b}{'legs'}
} keys %animals;
The cmp
and <=>
operators return one of three values (-1, 0, or 1) depending on whether the left argument is less than, equal to, or greater than the right argument. (cmp
does a string comparison, <=>
does a numeric one.) In Perl, 0 is false while -1 and 1 are true. If the cmp
returns a true value, the or
returns that value immediately, and sort
reorders the elements appropriately. If the cmp
returns false, the <=>
is evaluated and its result is returned instead.
When doing multi-layer sorts, it's common to use a "map-sort-map" technique (a.k.a. Schwartzian Transform):
@animals =
map { $_->[0] }
sort {
$a->[1] cmp $b->[1] ||
$a->[2] <=> $b->[2]
}
map { [$_, $animal{$_}{name}, $animal{$_}{legs}] }
keys %animal;
It's not as clear but because it usually has better performance it's a common idiom. This is especially important when the operands to the comparison are functions -- this technique prevents an unnecessary (and possibly expensive) recalculation for every comparison. For example, if you're sorting strings by length you only need to compute the length of each string once.