2009-06-17 148 views
3

我看到下面的代码的结果,但我不明白的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); 

回答

9

or是短路评估,所以如果它是真的(这是任何非零值),它将返回左侧的值,否则将评估右侧。

因此,在这种情况下,如果动物的名字相同(0 - 假),则为了排序目的将对腿的数量进行计数。

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返回false,则对<=>进行评估,并返回其结果。

当进行多层排序,这是通常使用“地图排序图”技术(又名Schwartzian Transform):

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

这并不清晰,而是因为它通常具有更好的性能,这是一个常见的成语。当比较的操作数是函数时,这一点尤其重要 - 这种技术可以防止每次比较都进行不必要的(可能是昂贵的)重新计算。例如,如果按长度排序字符串,则只需计算一次每个字符串的长度。

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;