在列表上下文中,这会对 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
函数是通过归并排序算法实现的。