在列表內容中,這會對 LIST 進行排序並傳回已排序的列表值。在標量內容中,sort
的行為未定義。
如果省略 SUBNAME 或 BLOCK,sort
會按照標準字串比較順序進行排序。如果指定 SUBNAME,它會提供一個子常式的名稱,該子常式會傳回小於、等於或大於 0
的數字值,具體取決於如何對列表的元素進行排序。(<=>
和 cmp
運算子在這種常式中非常有用。)SUBNAME 可以是標量變數名稱(未加下標),這種情況下,該值會提供實際要使用的子常式的名稱(或參考)。您可以提供一個 BLOCK 作為匿名內嵌排序子常式,來取代 SUBNAME。
如果子常式的原型是 ($$)
,要比較的元素會透過參考傳遞到 @_
中,就像一般的子常式一樣。這比未設定原型的子常式慢,因為要比較的元素會傳遞到子常式中,作為套件全域變數 $a
和 $b
(請參閱以下範例)。
如果子常式是 XSUB,要比較的元素會推送到堆疊中,這與通常傳遞參數到 XSUB 的方式相同。$a
和 $b
沒有設定。
要進行比較的數值總是透過參考傳遞,且不應變更。
您也無法使用 perlsyn 中所述的任何迴圈控制運算子,或使用 goto
退出排序區塊或子常式。
當 use locale
(但非 use locale ':not_characters'
)生效時,sort LIST
會根據目前的排序慣例來對 LIST 進行排序。請參閱 perllocale。
sort
會傳回原始清單中的別名,就像 for 迴圈的索引變數會對清單元素產生別名一樣。亦即,變更 sort
傳回清單中的元素(例如,在 foreach
、map
或 grep
中),實際上會變更原始清單中的元素。在撰寫明確的程式碼時,通常應避免這樣做。
Perl 在過去對於排序是否預設為穩定排序而有不同的變化。如果穩定性很重要,可以使用 sort 實用指令明確地加以控制。
範例
# sort lexically
my @articles = sort @files;
# same thing, but with explicit sort routine
my @articles = sort {$a cmp $b} @files;
# now case-insensitively
my @articles = sort {fc($a) cmp fc($b)} @files;
# same thing in reversed order
my @articles = sort {$b cmp $a} @files;
# sort numerically ascending
my @articles = sort {$a <=> $b} @files;
# sort numerically descending
my @articles = sort {$b <=> $a} @files;
# this sorts the %age hash by value instead of key
# using an in-line function
my @eldest = sort { $age{$b} <=> $age{$a} } keys %age;
# sort using explicit subroutine name
sub byage {
$age{$a} <=> $age{$b}; # presuming numeric
}
my @sortedclass = sort byage @class;
sub backwards { $b cmp $a }
my @harry = qw(dog cat x Cain Abel);
my @george = qw(gone chased yz Punished Axed);
print sort @harry;
# prints AbelCaincatdogx
print sort backwards @harry;
# prints xdogcatCainAbel
print sort @george, 'to', @harry;
# prints AbelAxedCainPunishedcatchaseddoggonetoxyz
# inefficiently sort by descending numeric compare using
# the first integer after the first = sign, or the
# whole record case-insensitively otherwise
my @new = sort {
($b =~ /=(\d+)/)[0] <=> ($a =~ /=(\d+)/)[0]
||
fc($a) cmp fc($b)
} @old;
# same thing, but much more efficiently;
# we'll build auxiliary indices instead
# for speed
my (@nums, @caps);
for (@old) {
push @nums, ( /=(\d+)/ ? $1 : undef );
push @caps, fc($_);
}
my @new = @old[ sort {
$nums[$b] <=> $nums[$a]
||
$caps[$a] cmp $caps[$b]
} 0..$#old
];
# same thing, but without any temps
my @new = map { $_->[0] }
sort { $b->[1] <=> $a->[1]
||
$a->[2] cmp $b->[2]
} map { [$_, /=(\d+)/, fc($_)] } @old;
# using a prototype allows you to use any comparison subroutine
# as a sort subroutine (including other package's subroutines)
package Other;
sub backwards ($$) { $_[1] cmp $_[0]; } # $a and $b are
# not set here
package main;
my @new = sort Other::backwards @old;
## using a prototype with function signature
use feature 'signatures';
sub function_with_signature :prototype($$) ($one, $two) {
return $one <=> $two
}
my @new = sort function_with_signature @old;
# guarantee stability
use sort 'stable';
my @new = sort { substr($a, 3, 5) cmp substr($b, 3, 5) } @old;
警告:對函式傳回的清單進行排序時,需要小心語法。如果您要對函式呼叫 find_records(@key)
傳回的清單進行排序,可以使用
my @contact = sort { $a cmp $b } find_records @key;
my @contact = sort +find_records(@key);
my @contact = sort &find_records(@key);
my @contact = sort(find_records(@key));
如果您要使用比較常式 find_records()
對陣列 @key
進行排序,可以使用
my @contact = sort { find_records() } @key;
my @contact = sort find_records(@key);
my @contact = sort(find_records @key);
my @contact = sort(find_records (@key));
$a
和 $b
會在呼叫 sort() 的套件中設定為套件全域變數。這表示在 main
套件中為 $main::a
和 $main::b
(或 $::a
和 $::b
),在 FooPack
套件中為 $FooPack::a
和 $FooPack::b
,依此類推。如果排序區塊在 $a
和/或 $b
的 my
或 state
宣告範圍內,您必須在排序區塊中寫出變數的完整名稱
package main;
my $a = "C"; # DANGER, Will Robinson, DANGER !!!
print sort { $a cmp $b } qw(A C E G B D F H);
# WRONG
sub badlexi { $a cmp $b }
print sort badlexi qw(A C E G B D F H);
# WRONG
# the above prints BACFEDGH or some other incorrect ordering
print sort { $::a cmp $::b } qw(A C E G B D F H);
# OK
print sort { our $a cmp our $b } qw(A C E G B D F H);
# also OK
print sort { our ($a, $b); $a cmp $b } qw(A C E G B D F H);
# also OK
sub lexi { our $a cmp our $b }
print sort lexi qw(A C E G B D F H);
# also OK
# the above print ABCDEFGH
妥善處理後,您可以混合套件和 my(或 state)$a
和/或 $b
my $a = {
tiny => -2,
small => -1,
normal => 0,
big => 1,
huge => 2
};
say sort { $a->{our $a} <=> $a->{our $b} }
qw{ huge normal tiny small big};
# prints tinysmallnormalbighuge
$a
和 $b
隱含地是 sort() 執行中的區域變數,且在完成排序後會回復其原先的數值。
使用 $a
和 $b
撰寫的排序子常式會繫結到其呼叫的套件。將它們定義在不同的套件中是可行的,但興趣不大,因為子常式仍必須參照呼叫套件的 $a
和 $b
package Foo;
sub lexi { $Bar::a cmp $Bar::b }
package Bar;
... sort Foo::lexi ...
使用原型版本(見上文)以取得更通用的替代方案。
比較函數必須有行為。如果它傳回不一致的結果(例如,有時說 $x[1]
小於 $x[2]
,有時又說相反),則結果未定義良好。
因為 <=>
在任一運算元為 NaN
(非數字)時傳回 undef
,因此在使用比較函數(例如 $a <=> $b
)對可能包含 NaN
的任何清單進行排序時要小心。以下範例利用 NaN != NaN
來從輸入清單中移除任何 NaN
。
my @result = sort { $a <=> $b } grep { $_ == $_ } @input;
在此版本的 perl 中,sort
函數是透過合併排序演算法實作的。