如何对数组进行排序,以便某些文件扩展名排在最前面?

时间:2010-03-26 17:36:30

标签: perl arrays

我有一个包含文件列表的数组。我想以一种方式对它进行排序,它会让我在数组的开头和之后的其余文件中包含.txt文件。

这就是我现在正在做的事情,它运作良好。

@files = (grep(/\.txt$/,@files),grep(!/\.txt$/,@files));

有没有更好的方法呢?

6 个答案:

答案 0 :(得分:10)

您询问了有关为多个文件扩展名执行此操作的后续评论。在那种情况下,我将建立Schwartzian变换。如果您是ST的新手,我推荐Joseph Hall在 Effective Perl Programming 中的解释。尽管Second Edition很快就会出现,但我们基本上保留了他的解释,因此first edition同样出色。谷歌图书似乎只显示第一版每页的一英寸,所以你在那里运气不好。

在这个答案中,我使用加权函数来决定哪些扩展应该移到顶部。如果扩展没有明确的权重,我只是用词典方式对它进行排序。您可以使用sort来获取您想要的订单:

@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";

答案 1 :(得分:5)

您只需在每个sort前面添加grep

 my @sorted =
   (
   sort( grep /\.txt\z/,   @files ),
   sort( grep ! /\.txt\z/, @files )
   );

这里的技巧是你要对列表进行分区,然后独立地对每个分区进行排序。根据您的操作,这可能比尝试在一个排序操作中执行所有操作要好得多。相反,它可能并不总是更好。

有许多其他方法可以完成这项工作,但它们并非如此简单。 :)

这是我的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,
     }
     );

答案 2 :(得分:5)

@sorted = sort { $b=~/\.txt$/ <=> $a=~/\.txt$/  ||  $a cmp $b } @files

首先放置.txt文件,然后按字母顺序排序(按字母顺序排列)。

@sorted = sort { $b=~/\.txt$/ <=> $a=~/\.txt$/ } @files

将首先放置.txt文件,否则保留原始订单(sort 稳定,因为Perl 5.8)

答案 3 :(得分:4)

Sort将可选块作为第一个参数,但在这种情况下,Schwartzian变换会更快。

@files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, !/\.txt$/ ] } @files;

答案 4 :(得分:3)

要有效地处理多个扩展,您可以通过在一次传递中对数组进行分区来修改brian d foy的已排序grep,然后单独对每个分区进行排序。

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};
    }
}

答案 5 :(得分:1)

代码高尔夫? 这不会产生令人讨厌的警告:

@files = map { $_->[0] } sort { @$b <=> @$a } map { [$_, /\.txt$/] } @files