Предполагая, что вы хотите отсчетов в порядке убывания и названия возрастания:
print map join(" ", @$_{qw/ count title /}) . "\n",
sort { $b->{count} <=> $a->{count}
||
$a->{title} cmp $b->{title} }
@$pets;
Это компактный код, написанный в функциональном стиле. Чтобы понять это, давайте посмотрим на эквивалентный код в более знакомом, императивном стиле.
Оператор Perl's sort
принимает необязательный параметр SUBNAME, который позволяет вам отмерить сравнение и дать ему имя, которое описывает, что он делает. Когда я это делаю, мне нравится начинать имя sub с by_
, чтобы сделать sort by_...
более естественным.
Для начала, вы могли бы написать
sub by_count_then_title {
$b->{count} <=> $a->{count}
||
$a->{title} cmp $b->{title}
}
my @sorted = sort by_count_then_title @$pets;
Обратите внимание, что не запятая следует подимени в таком виде!
Чтобы задать вопрос другому комментатору, вы можете использовать or
, а не ||
в by_count_then_title
, если считаете, что это более читаемо. Оба <=>
и cmp
have higher precedence (которые вы можете придумать как переплет более плотно), чем ||
и or
, так что это строго вопрос стиля.
Чтобы распечатать отсортированный массив, более знакомый выбор может быть
foreach my $p (@sorted) {
print "$p->{count} $p->{title}\n";
}
Perl использует $_
, если не указать переменную, которая получает каждое значение, поэтому следующее имеет тот же смысл:
for (@sorted) {
print "$_->{count} $_->{title}\n";
}
for
и foreach
ключевые слова являются синонимами, но я считаю, что вышеуказанные виды использования, т.е., foreach
если я буду называть вар iable или for
в противном случае, читайте наиболее естественно.
Используя map
, близкий кузен foreach
, а не сильно отличается:
map print("$_->{count} $_->{title}\n"), @sorted;
Вы также мог бы способствовать print
через map
:
print map "$_->{count} $_->{title}\n",
@sorted;
Наконец, чтобы избежать повторений $_->{...}
, hash slice@$_{"count", "title"}
дает нам значения, связанные с счетчиком и заголовком в текущей записи цикла.Имея значения, мы должны join их с одним пробелом и добавить новую строку к результату, так
print map join(" ", @$_{qw/ count title /}) . "\n",
@sorted;
Помните, что qw//
является сокращением для написания списка строк. Как показано в этом примере, прочитайте выражение map
back-to-front (или снизу вверх, как я его отступил): сначала отсортируйте записи, затем отформатируйте их и распечатайте.
Вы могли бы устранить временный @sorted
но назвать имя сравнение:
print map join(" ", @$_{qw/ count title /}) . "\n",
sort by_count_then_title
@$pets;
Если применение join
просто слишком многословно на свой вкус, то
print map "@$_{qw/ count title /}\n",
sort by_count_then_title
@$pets;
Что «карта» означает/делать в коде выше? И почему нам не нужно было указывать два атрибута в двух разных наборах фигурных скобок? – syker
@syker: 'map' берет каждый хэш в отсортированном массиве и выводит значения' count' и 'title' в строке, разделенной пробелами. @gbacon: Может быть, стоит объяснить, есть ли разница между использованием '||' и 'или' в вашем роде, поскольку я всегда вижу '||' в примерах типа-чего-нибудь. Есть ли вероятность того, что более читаемые 'или' ведут себя по-другому при любых обстоятельствах? – Zaid
@syker @Zaid См. Обновленный ответ. –