сортировать в лексикографическом порядке

Я вижу результаты следующего кода, но не понимаю, как именно 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);

person mleykamp    schedule 17.06.2009    source источник


Ответы (3)


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

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

person Blair Conrad    schedule 17.06.2009

Подгруппа сортировки (материал в {} после 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 ложно, а -1 и 1 истинны. Если cmp возвращает истинное значение, or немедленно возвращает это значение, а sort соответствующим образом переупорядочивает элементы. Если cmp возвращает false, оценивается <=> и вместо этого возвращается его результат.

При многослойной сортировке обычно используется метод «карта-сортировка-карта» (также известный как преобразование Шварца). ):

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

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

person Michael Carman    schedule 17.06.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;
person ephemient    schedule 17.06.2009