сортировать по лексикографическому порядку - PullRequest
3 голосов
/ 18 июня 2009

Я вижу результаты из следующего кода, но я не совсем понимаю, как or знает, что делать в следующем sort примере:

use Data::Dumper;

$animals{'man'}{'name'} = 'paul';
$animals{'man'}{'legs'} = 2;
$animals{'cheeta'}{'name'} = 'mike';
$animals{'cheeta'}{'legs'} = 3;
$animals{'zebra'}{'name'} = 'steve';
$animals{'zebra'}{'legs'} = 4;
$animals{'cat'}{'name'} = '';
$animals{'cat'}{'legs'} = 3;
$animals{'dog'}{'name'} = '';
$animals{'dog'}{'legs'} = 4;
$animals{'rat'}{'name'} = '';
$animals{'rat'}{'legs'} = 5;

@animals = sort {
      $animals{$a}{'name'} cmp $animals{$b}{'name'}
   or $animals{$a}{'legs'} <=> $animals{$b}{'legs'}
} keys %animals;

print Dumper(\@animals);

Ответы [ 3 ]

14 голосов
/ 18 июня 2009

Sortsub (материал в {} после sort) определяет двухуровневую сортировку: сначала по имени, а затем по количеству ветвей. or реализует перекрестный переход между двумя критериями. Проще увидеть, если вы отформатируете код по-другому:

@animals = sort {
    $animals{$a}{'name'} cmp $animals{$b}{'name'} or
    $animals{$a}{'legs'} <=> $animals{$b}{'legs'}
} keys %animals;

Операторы cmp и <=> возвращают одно из трех значений (-1, 0 или 1) в зависимости от того, является ли левый аргумент меньше, равен или больше правого аргумента. (cmp выполняет сравнение строк, <=> - числовое.) В Perl 0 равно false, а -1 и 1 - true. Если cmp возвращает истинное значение, or немедленно возвращает это значение, и sort соответствующим образом переупорядочивает элементы. Если cmp возвращает false, <=> оценивается и вместо него возвращается его результат.

При выполнении многослойных сортировок обычно используется метод "map-sort-map" (a.k.a. Преобразование Шварца ):

@animals =
  map  { $_->[0] }
  sort {
    $a->[1] cmp $b->[1] ||
    $a->[2] <=> $b->[2]
  }
  map { [$_, $animal{$_}{name}, $animal{$_}{legs}] }
  keys %animal;

Это не так ясно, но, поскольку он обычно имеет лучшую производительность, это обычная идиома. Это особенно важно, когда операнды сравнения являются функциями - этот метод предотвращает ненужный (и, возможно, дорогой) пересчет для каждого сравнения. Например, если вы сортируете строки по длине, вам нужно вычислить длину каждой строки только один раз.

9 голосов
/ 18 июня 2009

or является оценщиком короткого замыкания, поэтому он вернет значение левой части, если оно истинно (любое ненулевое значение), и в противном случае оценит правую часть.

Таким образом, в этом случае, если имена животных сравниваются как равные (0 - ложь), число ног будет подсчитано для целей сортировки.

2 голосов
/ 18 июня 2009

Могу ли я предложить Sort::Key как альтернативу настоящему коду в целом?

use Sort::Key::Multi qw(sikeysort);  # sort keyed on (string, integer)
@animals = sikeysort { $animals{$_}{name}, $animals{$_}{legs} } keys %animals;

# alternately,
use Sort::Key::Maker sort_by_name_then_legs =>
    sub { $animals{$_}{name}, $animals{$_}{legs} }, qw(string integer);
@animals = sort_by_name_then_legs keys %animals;
...