徐小玉的博客。
分类:
2008-05-23 12:23:25
Perl的排序
Perl好用的原因之一在于它的灵活性,这一点来源于很多小的好处。其中排序功能是很常用的:my %hashNames = { '1' => 'Sharon Basque-Dion',
要对上面关联数组里的值进行排序,可以用sort:
'2' => 'Dave Schuman',
'3' => 'Sally Waltson',
'0x10' => 'Rodney Wiederman',
'0x1F' => 'Angus O'Hare',
'0x2A' => 'Jean-Pascal Conteau' };foreach my $szName (sort values %hashNames){
print $szName;
}
当然反向排序也是有的,用reverse sortforeach my $szName (reverse sort values %hashNames){
Perl缺省的sort是区分大小写的ASCII码排序。如果需要其它排序方式,例如不分大小写的字母排序怎么办?
print $szName;
}
当进入sort情景时,Perl提供两个全局变量,分别叫$a和$b,指向待比较的两个值。注意不要去改变它们的值,我试着改过,结果就是得到的结果面目全非。在sort关键词的后面,可以跟一个用于比较的函数名或者一段用花括号括起来的代码。另外,Perl提供比较两个操作符,一个是cmp(比较操作符), $a cmp $b
的结果是$a和$b按ASCII顺序排序得到的结果1,0,-1(大,等于,小)。另外一个是<=>(星船操作符,starship operator),$a <=> $b
返回$a和$b按数值大小比较的结果1,0,-1(代表大,等于,小)。确省情况下,sort是等同于sort { $a cmp $b}
的,Perl是利用归并(merge)算法和ASCII比较来排序。更详细的语法说明见。
假如我们需要按字典顺序对人名进行排序,可以写一个自己的排序函数,其中利用到$a, $b, cmp或者<=>。其实最后的返回值只要符合1,0,-1的定义就可以。
sub by_dictionary_sequence{
my $aa = lc $a; #得到小写形式
my $bb = lc $b;
$aa =~ s/[\W_]+//g; #去掉非字典字符
$aa =~ s/[\W_]+//g;
$aa cmp $bb;
}
foreach my $szName (sort by_dictionary_sequence values %hashNames){
print $szName;
}
我习惯使用by_...作为比较函数的名字,这样跟在sort后面正好是一句话。Perl会使用sort后跟的函数来进行元素的两两比较,代替缺省的{ $a cmp $b}。要使用字典降序排列的话,只要把dictionary_sequence的最后一句里的$aa和$bb换个位置就行。事实上系统提供了$a和$b以及sort后可跟函数的功能以后,如何进行比较就已经不是 Perl语言本身要关注的内容了。例如前面的%hashNames,我们看到它的键是10进制和16进制数的混合,可以这样来排序:sub by_mix_numeric{
my $aa = $a;
my $bb = $b;
$aa = $1 if($aa =~ /0x(.*)/);
$bb = $1 if($bb =~ /0x(.*)/);
$aa = hex( $aa); $bb = hex($bb);
$aa <=> $bb;
}
foreach my $szIndex (sort by_mix_numeric keys %hashNames){
print $szIndex ;
}
更多见的一个例子是数据库排序,先按最重要列排序,如果相等则再看次重要列等等,这基本是体力活,虽然说数据库排序也还是很有技巧的,比如Orcish算法,Schwartzian转换和Guttman-Rosler转换(GRT)等等,那已经超出我的兴趣范围了,详细内容可以看 。 如果真需要用,我可能会考虑CPAN上的Sort::Fields模块。
最后要罗嗦一句的是,如果不想给自己找烦恼的话,永远都不要自定义变量$a和$b。也许这就是为什么C#向Perl学习了foreach结构却的原因:C#的框架里没有这样的缺省变量的位置。