2010-03-26 16 views
3

我有一個包含文件列表的數組。我想對它進行排序,讓它在數組的開頭和文件的其餘部分都有.txt文件。如何對數組進行排序,以便某些文件擴展名排在最前面?

這就是我現在正在做的,它工作正常。

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

有沒有更好的方法來做到這一點呢?

+0

這是O(n),而下面的種類是O(n log n)。如果你想分區而不是排序,那麼這是一個更好的選擇。但是,如果您想分類,請在一次操作中完成所有操作。 – jrockway 2010-03-27 00:46:01

回答

4

Sort將可選塊作爲第一個參數,儘管在這種情況下Schwartzian變換會更快。

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

在我的測試中,我發現Schwartzian變換稍慢(但只是一點點)。在我的回答中,我必須在陣列上做兩遍,但在你的例子中,你也是。你也必須提供參考。 – 2010-03-26 17:54:00

+0

我第一次聽說Schwartzian變換。這絕對有趣。假設我首先需要txt文件,然後是rpm,然後是其他文件。上面的代碼如何改變?我不確定我明白它究竟在做什麼。 – rarbox 2010-03-26 18:08:44

+0

@rarbox:參見http://stackoverflow.com/questions/594257/when-are-schwartzian-transforms-useful – Ether 2010-03-26 18:13:05

5

你只需要在每個grep S的前面加上sort

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, 
    } 
    ); 
+0

在所有這些中,我認爲這可能是最清潔和最明顯的。 – 2010-03-26 18:08:56

+1

根據他的後續評論想要分類更多文件擴展名,這可能不是正確的答案。 – 2010-03-26 18:15:04

5

 

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

會放。 txt文件,並按照字典順序排列(按字母順序排列)。

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

會先將.txt文件否則保留原來的順序(sort是因爲Perl 5.8 穩定

1

代碼高爾夫? 這不會產生討厭的警告:

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

Nah,我不玩Code Golf。我正在用Net :: FTPSSL編寫一個FTP客戶端,遇到了需要按特定順序下載文件的情況,我想知道是否有更好的方法來處理,而不是我已經做的。謝謝回答。 – rarbox 2010-03-26 18:11:31

+0

我幾乎可以肯定@ $ b應該是錯的,但我不能讓這個例子不起作用。在我看來,@ $ b應該被強制爲一個數字,而不是比較數組中的某些東西,但我想這不會發生。它爲什麼有效? – 2010-03-26 18:41:58

+0

@brian d foy - 如果/\.txt$/匹配,則給出1,並且對數組的引用將包含類似['foo.txt',1];如果沒有,/..txt /產生空的列表,這反過來給像['foo.bin']這樣的數組引用。正如你所看到的,匹配結果的數組將包含2個元素,否則 - 1個元素。而且,是的,你是對的,在排序的塊中它被強制進入陣列中的許多元素。 – zakovyrya 2010-03-27 04:50:07

10

你問到這樣一個以上的文件擴展名的後續評論。在那種情況下,我會建立Schwartzian變換。如果你是ST的新手,我建議Joseph Hall的解釋在有效的Perl編程。儘管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"; 
3

高效處理多個擴展,你可以通過在一個通分割你的陣列修改布賴恩·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}; 
    } 
} 
+1

非常好!每次我看到part()的時候,我都希望他把它命名爲partition():) – 2010-03-26 20:56:19