2009-06-17 151 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;