Как я могу отсортировать массив так, чтобы определенные расширения файлов сортировались по верху? - PullRequest
4 голосов
/ 26 марта 2010

У меня есть массив, содержащий список файлов.Я хочу отсортировать его так, чтобы он позволял мне иметь файлы .txt в начале массива, а остальные файлы после этого.

Это то, что я делаю сейчас, и это прекрасно работает.

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

Есть ли лучший способ сделать это?

Ответы [ 6 ]

10 голосов
/ 26 марта 2010

Вы запросили дополнительный комментарий о том, как сделать это для нескольких расширений файлов. В этом случае я бы построил преобразование Шварца. Если вы новичок в ST, я рекомендую объяснение Джозефа Холла в Эффективное программирование на Perl . Хотя Second Edition выйдет очень скоро, мы в основном оставили его объяснение, так что first edition так же хорош. Google Книги, кажется, показывают только один дюйм каждой страницы для первого издания, поэтому вам не повезло.

В этом ответе я использую весовую функцию, чтобы решить, какие расширения следует переместить в начало. Если расширение не имеет явного веса, я просто сортирую его лексикографически. Вы можете дурачиться с 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";
5 голосов
/ 26 марта 2010

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

помещает файлы .txt первыми, а в противном случае сортирует лексикографически (по алфавиту).

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

будет помещать файлы .txt первыми и в противном случае сохранять исходный порядок (sort - это стабильный с момента Perl 5.8)

5 голосов
/ 26 марта 2010

Вам просто нужно добавить sort перед каждым из ваших grep s:

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

Хитрость в том, что вы разбиваете список на части, а затем сортируете каждый раздел независимо. В зависимости от того, что вы делаете, это может быть намного лучше, чем пытаться делать все в одной операции сортировки. И наоборот, это не всегда может быть лучше.

Существуют и другие способы сделать это, но они не так просты. :)

Вот быстрый тест на моем MacBook Air с ванильным 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,
     }
     );
4 голосов
/ 26 марта 2010

Сортировка принимает необязательный блок в качестве первого аргумента, хотя в этом случае преобразование Шварца будет быстрее.

@files = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, !/\.txt$/ ] } @files;
3 голосов
/ 26 марта 2010

Для эффективной обработки нескольких расширений вы можете изменить сортировку Брайана Д Фоя 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};
    }
}
1 голос
/ 26 марта 2010

код гольф? Это не вызовет неприятных предупреждений:

@files = map { $_->[0] } sort { @$b <=> @$a } map { [$_, /\.txt$/] } @files
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...