Сортировка по нескольким ключам

Мы забыли о четвертом игроке, который отправился в боулинг с первыми тремя. На самом деле содержимое хеша выглядит так:

my %score = (
"barney" => 195, "fred" => 205,
"dino" => 30, "bamm-bamm" => 195,
);

Как видите, bamm-bamm набрал одинаковое количество очков с barney. Кто же из них должен стоять первым в отсортированном списке игроков? Неизвестно, потому что оператор сравнения (обнаружив одинаковые значения с обеих сторон) вернет нуль при проверке этой пары. Возможно, это несущественно, и все же желательно, чтобы сортировка
осуществлялась в четко определенном, предсказуемом порядке. Если несколько игроков имеют одинаковое количество очков, они должны находиться вместе в списке. Однако внутри этой группы имена должны быть упорядочены в ASCII-алфавитном порядке. Но как выразить это в функции сортировки? И снова задача решается относительно просто:

my @winners = sort by_score_and_name keys %score;
sub by_score_and_name {
$score{$b} <=> $score{$a} # Числовая сортировка по убыванию
# набранных очков
or
$a cmp $b # ASCII-алфавитная сортировка по именам
}

Как работает это решение? Когда оператор <=> видит два разных счета, использоваться должен именно этот критерий сравнения.


Оператор возвращает –1 или 1 (истинное значение), низкоприоритетная ускоренная обработка or пропускает оставшуюся часть выражения, и функция выдает нужный результат (напомним, что ускоренная обработка or возвращает последнее вычисленное выражение). Но если оператор <=> видит два одинаковых счета, он возвращает 0 (ложное значение). Управление передается оператору cmp, который возвращает нужный результат со сравнением ключей в строковом виде. Иначе говоря, в случае совпадения счетов «ничья» разрешается посредством сравнения строк. Мы знаем, что при таком использовании функция сортировки by_score_and_name никогда не вернет 0. Итак, мы знаем, что порядок сортировки всегда четко определен; сегодняшний результат полностью идентичен тому, который мы получим завтра с теми же данными.

Конечно, ваша функция сортировки не ограничивается всего двумя уровнями. Ниже приведена программа для библиотеки, упорядочивающая список читателей с сортировкой по пяти уровням. Данные сортируются по величине штрафов за просрочку (вычисляемых функцией -fine, которая здесь не приводится), количеству взятых книг (из хеша -items), полным именам (сначала фамилия, затем имя – и то, и другое берется из хешей) и, наконец, по идентификаторам, если все остальные атрибуты совпадают:

@patron_IDs = sort {
&fines($b) <=> &fines($a) or
$items{$b} <=> $items{$a} or
$family_name{$a} cmp $family_name{$a} or
$personal_name{$a} cmp $family_name{$b} or
$a <=> $b
} @patron_IDs;

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Perl на эту тему:
Использование функции sprintf для вывода денежных сумм
Операции с подстроками и функция substr
Поиск подстроки по индексу
Расширенная сортировка
Сортировка хеша по значениям

Вернуться в раздел: Perl / 13. Строки и сортировка