Perl 如何对数组进行排序,使某些文件扩展名排序到顶部?
我有一个包含文件列表的数组。我想对它进行排序,这样它就可以在数组的开头有.txt文件,之后还有其他文件 这就是我现在正在做的,效果很好Perl 如何对数组进行排序,使某些文件扩展名排序到顶部?,perl,arrays,Perl,Arrays,我有一个包含文件列表的数组。我想对它进行排序,这样它就可以在数组的开头有.txt文件,之后还有其他文件 这就是我现在正在做的,效果很好 @files = (grep(/\.txt$/,@files),grep(!/\.txt$/,@files)); 但是有更好的方法吗?排序将可选块作为第一个参数,不过在这种情况下,施瓦茨变换会更快 @files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, !
@files = (grep(/\.txt$/,@files),grep(!/\.txt$/,@files));
但是有更好的方法吗?排序将可选块作为第一个参数,不过在这种情况下,施瓦茨变换会更快
@files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, !/\.txt$/ ] } @files;
@files=map{$\->[0]}sort{$a->[1]$b->[1]}map{[$\u,!/\.txt$/]}文件;
您只需在每个s前面添加一个:
这里的技巧是对列表进行分区,然后对每个分区进行独立排序。根据您正在做的事情,这可能比尝试在一个排序操作中完成所有操作要好得多。相反,它可能并不总是更好
有多种其他方法可以完成这一任务,但它们并不是这么简单。:)
下面是我的MacBook Air和vanilla Perl 5.10.1的快速基准测试:
There are 600 files to sort
brian: 3 wallclock secs @ 369.75/s (n=1161)
control: 3 wallclock secs @ 1811.99/s (n=5744)
leon: 4 wallclock secs @ 146.98/s (n=463)
mobrule: 3 wallclock secs @ 101.57/s (n=324)
sort: 4 wallclock secs @ 559.62/s (n=1746)
以下是脚本:
use Benchmark;
use vars qw(@files);
@files = qw(
buster.pdf
mimi.xls
roscoe.doc
buster.txt
mimi.txt
roscoe.txt
) x 100;
printf "There are %d files to sort\n", scalar @files;
sub leon {
my @sorted =
map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [ $_, !/\.txt$/ ]
} @files;
}
sub brian {
my @sorted =
(
sort( grep /\.txt\z/, @files ),
sort( grep ! /\.txt\z/, @files )
);
}
sub mobrule {
my @sorted =
sort { ($b=~/\.txt\z/) <=> ($a=~/\.txt\z/) || $a cmp $b }
@files;
}
sub plain_sort {
my @sorted = sort @files;
}
sub control {
my @sorted = @files;
}
timethese( -3,
{
brian => \&brian,
leon => \&leon,
mobrule => \&mobrule,
control => \&control,
sort => \&plain_sort,
}
);
使用基准;
使用vars qw(@files);
@文件=qw(
buster.pdf
mimi.xls
罗斯科博士
buster.txt
mimi.txt
roscoe.txt
)x100;
printf“有%d个文件要排序\n”,scalar@files;
次里昂{
我的@sorted=
映射{$\->[0]}
排序{$a->[1]$b->[1]}
映射{[$\,!/\.txt$/]
}@文件;
}
亚布莱恩{
我的@sorted=
(
排序(grep/\.txt\z/,@files),
排序(grep!/\.txt\z/,@files)
);
}
次级规则{
我的@sorted=
排序{($b=~/\.txt\z/)($a=~/\.txt\z/)| |$a cmp$b}
@档案;
}
亚平原排序{
我的@sorted=sort@文件;
}
子控制{
我的@sorted=@文件;
}
这些时间(-3,
{
布莱恩=>\&布莱恩,
leon=>\&leon,
mobrule=>\&mobrule,
控件=>\&控件,
排序=>\&普通\u排序,
}
);
@sorted=sort{$b=~/\.txt$/$a=~/\.txt$/\\124; a cmp$b}@文件
将把.txt文件放在第一位,否则按字典顺序(字母顺序)排序
@sorted=sort{$b=~/\.txt$/$a=~/\.txt$/}@文件
将先放置.txt文件,否则将保留原始顺序(sort
自Perl 5.8以来是稳定的)代码?
这不会产生令人讨厌的警告:
@files = map { $_->[0] } sort { @$b <=> @$a } map { [$_, /\.txt$/] } @files
@files=map{$\->[0]}排序{$$b@$a}映射{[$\,/\.txt$/]}@files
您询问了关于对多个文件扩展名执行此操作的后续评论。在这种情况下,我将构建Schwartzian变换。如果您是ST新手,我推荐Joseph Hall关于有效Perl编程的解释。虽然这本书很快就要出版了,但我们基本上还是保留了他的解释,所以这本书也一样好。谷歌图书(GoogleBooks)第一版似乎只显示每页一英寸的内容,所以你在那里运气不好
在这个答案中,我使用加权函数来决定哪些扩展应该移到顶部。如果一个扩展没有明确的权重,我就按词法进行排序。您可以通过以下方式获得您想要的订单:
@files = qw(
buster.pdf
mimi.xls
roscoe.doc
buster.txt
mimi.txt
roscoe.txt
buster.rpm
mimi.rpm
);
my %weights = qw(
txt 10
rpm 9
);
my @sorted =
map { $_->{name} }
sort {
$b->{weight} <=> $a->{weight}
||
$a->{ext} cmp $b->{ext}
||
$a cmp $b
}
map {
my( $ext ) = /\.([^.]+)\z/;
{ # anonymous hash constructor
name => $_,
ext => $ext,
weight => $weights{$ext} || 0,
}
}
@files;
$" = "\n";
print "@sorted\n";
@files=qw(
buster.pdf
mimi.xls
罗斯科博士
buster.txt
mimi.txt
roscoe.txt
buster.rpm
mimi.rpm
);
我的重量百分比=qw(
txt 10
转速9
);
我的@sorted=
映射{$\->{name}
排序{
$b->{weight}$a->{weight}
||
$a->{ext}cmp$b->{ext}
||
$a$b
}
地图{
我的($ext)=/\([^.]+)\z/;
{#匿名哈希构造函数
名称=>$\,
ext=>$ext,
权重=>$weights{$ext}| | 0,
}
}
@档案;
$“=”\n”;
打印“@sorted\n”;
为了有效地处理多个扩展,您可以通过一次对数组进行分区来修改brian d foy的排序grep
s,然后对每个分区进行独立排序
use strict;
use warnings;
use List::MoreUtils qw(part);
my @files = qw(
bar Bar.pm bar.txt
bar.jpeg foo foo.pm
foo.jpeg zebra.txt zebra.pm
foo.bat foo.c foo.pl
Foo.pm foo.png foo.tt
orange apple zebra.stripe
);
my @parts = part { get_extension_priority($_) } @files;
my @sorted = map { sort( @{ $_ || [] } ) } @parts;
print map "$_\n", @sorted;
BEGIN {
# Set extension priority order
my @priority = qw( stripe txt nomatch pl jpeg );
# make a hash to look up priority by extension
my %p = map { $priority[$_], $_ } 0..$#priority;
sub get_extension_priority {
my $file = shift;
return scalar @priority
unless /[.](\w*)$/;
return scalar @priority
unless exists $p{$1};
return $p{$1};
}
}
在我的测试中,我发现Schwartzian变换有点慢(但只有一点).在我的回答中,我必须对数组进行两次遍历,但在你的示例中,你也是。你也必须进行引用。我第一次听说Schwartzian变换。它确实很有趣。假设我先要txt文件,然后是rpm,然后是其他文件。上面的代码需要如何更改?我不确定我是否需要更改了解它到底在做什么。@rarbox:看看所有这些,我认为这可能是最干净和最明显的。根据他关于想要排序更多文件扩展名的后续评论,这可能不是正确的答案。不,我不是在玩代码高尔夫。我在用Net::FTPSSL编写一个FTP客户端,遇到了一个我需要的情况ded文件以特定的顺序下载,我想知道是否有比我现在做的更好的排序方法。感谢您的回答。我几乎可以肯定@$b应该是错误的,但我不能让这个示例不起作用。在我看来,@$b应该被强制为一个数字,而不是比较数组中的某些内容,但我想这不会发生ing.Why它能工作?@brian d foy-如果/\.txt$/匹配,它将给出1,对数组的引用结果将包含类似['foo.txt',1]的内容;如果不匹配,则/\.txt$/将生成空列表,而空列表又会给出对类似['foo.bin'的数组的引用。正如您所看到的,具有匹配结果的数组将包含2个元素,否则为-1个元素。而且,是的,您是对的,在sort的块中,它被强制为数组中的许多元素。啊,很棘手。非常好。但是,我不会将其列为好的样式。:@brian d foy-是的,我不会将其投入生产。非常好!每次我看到part()虽然,我希望他刚才把它命名为partition():)这是O(n),而下面的排序是O(n log n)。如果你想要一个分区而不是一个排序,那么它是一个更好的选择。如果你想要排序,尽管,在一个操作中完成这一切。
@files = map { $_->[0] } sort { @$b <=> @$a } map { [$_, /\.txt$/] } @files
@files = qw(
buster.pdf
mimi.xls
roscoe.doc
buster.txt
mimi.txt
roscoe.txt
buster.rpm
mimi.rpm
);
my %weights = qw(
txt 10
rpm 9
);
my @sorted =
map { $_->{name} }
sort {
$b->{weight} <=> $a->{weight}
||
$a->{ext} cmp $b->{ext}
||
$a cmp $b
}
map {
my( $ext ) = /\.([^.]+)\z/;
{ # anonymous hash constructor
name => $_,
ext => $ext,
weight => $weights{$ext} || 0,
}
}
@files;
$" = "\n";
print "@sorted\n";
use strict;
use warnings;
use List::MoreUtils qw(part);
my @files = qw(
bar Bar.pm bar.txt
bar.jpeg foo foo.pm
foo.jpeg zebra.txt zebra.pm
foo.bat foo.c foo.pl
Foo.pm foo.png foo.tt
orange apple zebra.stripe
);
my @parts = part { get_extension_priority($_) } @files;
my @sorted = map { sort( @{ $_ || [] } ) } @parts;
print map "$_\n", @sorted;
BEGIN {
# Set extension priority order
my @priority = qw( stripe txt nomatch pl jpeg );
# make a hash to look up priority by extension
my %p = map { $priority[$_], $_ } 0..$#priority;
sub get_extension_priority {
my $file = shift;
return scalar @priority
unless /[.](\w*)$/;
return scalar @priority
unless exists $p{$1};
return $p{$1};
}
}