按字典顺序排序

3

我看到了以下代码的结果,但我不太明白在下面的sort示例中or如何知道要执行什么操作:

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

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为假,而-1和1为真。如果cmp返回一个真值,or会立即返回该值,sort将重新排列元素。如果cmp返回假值,则计算<=>并返回其结果。

在进行多层排序时,通常使用“映射-排序-映射”技术(也称为Schwartzian Transform):

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

虽然不是很明显,但由于通常具有更好的性能,这是一种常见的习惯用法。当比较的操作数是函数时,尤其重要--该技术可以防止为每个比较执行不必要(可能很昂贵)的重新计算。例如,如果您按长度对字符串进行排序,则只需要计算每个字符串的长度一次。


9

or是一种短路运算符,如果左侧为真(任何非零值),则返回左侧的值,否则会计算右侧的值。

因此,在这种情况下,如果动物名称相等(0 - false),将计算腿的数量以进行排序。


2

我可以建议使用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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接