2010-06-29 2 views
9

После выполнения этих строк в Perl:Сортировка массива Ссылки на Хэш

my $data = `curl '$url'`; 
my $pets = XMLin($data)->(pets); 

У меня есть ссылка на массив, содержащую ссылки на хеши:

$VAR1 = [ 
     { 
     'title' => 'cat', 
     'count' => '210' 
     }, 
     { 
     'title' => 'dog', 
     'count' => '210' 
     } 
] 

В Perl, как сортировать хэш сначала по счету и вторично по названию. Затем напечатайте STDOUT счетчик, за которым следует название в каждой новой строке.

ответ

9

Предполагая, что вы хотите отсчетов в порядке убывания и названия возрастания:

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, если считаете, что это более читаемо. Оба <=> и cmphave 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; 
+0

Что «карта» означает/делать в коде выше? И почему нам не нужно было указывать два атрибута в двух разных наборах фигурных скобок? – syker

+0

@syker: 'map' берет каждый хэш в отсортированном массиве и выводит значения' count' и 'title' в строке, разделенной пробелами. @gbacon: Может быть, стоит объяснить, есть ли разница между использованием '||' и 'или' в вашем роде, поскольку я всегда вижу '||' в примерах типа-чего-нибудь. Есть ли вероятность того, что более читаемые 'или' ведут себя по-другому при любых обстоятельствах? – Zaid

+0

@syker @Zaid См. Обновленный ответ. –

Смежные вопросы