2011-03-05 98 views
1

嗨, 我有這個文件稱爲電話簿在Perl排序的第二個字

Steve Blenheim:239-923-7366:238-934-7865:95 Latham Lane, Easton, PA 83755:11/12/56:20300 
Betty Boop:245-836-8357:245-876-7656:635 Cutesy Lane, Hollywood, CA 91464:6/23/23:14500 
Igor Chevsky:385-375-8395:385-333-8976:3567 Populus Place, Caldwell, NJ 23875:6/18/68:23400 
Norma Corder:397-857-2735:397-857-7651:74 Pine Street, Dearborn, MI 23874:3/28/45:245700 

,我試圖從第二個字的字母順序文本(姓)排序,並沒有能夠找出如何做到這一點。我正在從文件中讀取這樣做

open (FILE, phonebook); 
    @line = <FILE>; 
    close(FILE); 

有什麼想法嗎?我可以按字母順序對第一個字段進行排序並反轉,但似乎無法使第二個字段正確排序。 在此先感謝

+5

所以你想排序「比利鮑勃託頓」基於鮑勃?和「博士馬丁路德金Jr「基於」馬丁「?排序很容易。最難的問題是如何確定哪個是姓... – tadmc 2011-03-05 02:09:49

回答

2

如果您不介意使用shell,sort -r -k2會按相反順序對文件進行排序。

0

您需要逐行讀取文件來完成該操作。事情是這樣的:

my %list; 
open(FILE, phonebook); 
while(<FILE>){ 
    my @vals = split(/:/, $_); 
    (my $key = $vals[0]) =~ s/(\S+)\s+(.+)/$2 $1/; # split first field, reverse word order 
    $list{$key} = $_; #save row keyed on $key 
} 

foreach my $key(sort {$b cmp $a} keys(%list)){ 
    print $list{$key}; 
} 
+0

糟糕的主意,如果有任何重複的第二領域。不要只是假設它是一個合適的關鍵: ) – hobbs 2011-03-05 02:13:56

+0

@hobbs:但我的例子中的關鍵字是第二個和所有後續詞,空格,第一個詞(第一個以冒號分隔的字段),如果在此示例電話簿中重複,則所有投注都關閉。 – RET 2011-03-05 02:17:48

+0

啊,我明白了,這真是一件可怕的事情,但是沒關係, – hobbs 2011-03-05 02:19:26

10

我分享tadmc的關切,認爲第二場,由空格並不總是將是姓,但回答的問題,因爲它涉及到第二場,你可以使用split得到它,你可以這樣排序:

簡單但可怕的緩慢的版本(易於閱讀,但它重新分裂每一個領域每一次比較兩條線,這是低效率)。

@lines = sort { # Compare second fields 
    (split " ", $a)[1] 
    cmp 
    (split " ", $b)[1] 
} @lines; 

Schwartzian transform版本(不完全一樣的事情與前一個,只有更快):

@lines = map { # Get original line back 
    $_->[0] 
} sort { # Compare second fields 
    $a->[1] cmp $b->[1] 
} map { # Turn each line into [original line, second field] 
    [ $_, (split " ", $_)[1] ] 
} @lines; 
+0

+1如果你擺脫了可怕的第一個例子或將其標記爲「這是做錯的方式」並解釋原因。 – converter42 2011-03-05 02:38:47

0

我認爲這是有趣的現代Perl的方式來寫(該解決方案是相同的),這是完整的腳本:

use strict; 

open my $FILE, '<', 'phonebook'; 
my @lines = <$FILE>; 

my @sorted = sort { 
       my @a = split(/\s+/,$a); 
       my @b = split(/\s+/,$b); 
       $b[1] cmp $a[1] } @lines; 

foreach my $item(@sorted) { 
    print "$item\n"; 
} 

close $FILE; 
+0

使用strict和3參數open的榮譽一個詞彙文件夾ndle。你的代碼有幾個問題。首先,你有一個拼寫錯誤,你錯過了你的排序塊上的大括號,並沒有輸入@ @ lines'數組。其次,排序是一項昂貴的操作,最多運行O(n),最差O(n^2),平均運行O(n * log n)。這意味着你將進行大量的比較,並且每次重新分割關鍵字。預先計算您的搜索條件會更好。您可以使用Schwartzian變換或使用中間變量。預先計算確實值得。 – daotoad 2011-03-06 17:02:04

0

我很驚訝沒有人提到這一點,但如果我們整理電話簿,我們可能真的不希望一個純ASCII排序。

請問Bob DeCarlo真的屬於Ralph Dearborn之前嗎?如果您使用cmp排序,DeCarlo先生在結果中排​​名第一。

即使你規範化的情況下,你仍然有問題。排序和歸檔有很多複雜的問題。 handling這些問題不同organizations have rules

由於排序是一項昂貴的操作,因此您需要儘可能快地完成每項比較。這樣做的方法是使用最簡單的代碼來進行所有比較。由於cmp本身不會給我們期望的結果,因此我們需要爲電話簿中的每個項目生成並緩存規範化的排序項。

因此,假設你已經有了一個數組電話簿數據:

sub extract_and_normalize { 
    # Do stuff here to embody your alphabetization rules. 

    return [ $normed, $line ]; 
} 

# Generate your sort terms 
my @processed = map extract_and_normalize($_), @lines; 

# Sort by the normalized values 
my @sorted = sort {$a->[0] cmp $b->[0]}, @processed; 

# Extract the lines from the sorted set. 
@lines = map $_->[1], @sorted; 

或者使用的Schwartzian變換,如hobbs建議,以避免使所有的中間變量:

@lines = map $_->[1], 
     sort { $a->[0] cmp $b->[0] } 
     map extract_and_normalize($_), @lines; 
2

基於Miguel Prz解決方案,我將'cmd'替換爲'< =>'。 這對數字很重要。如果使用CMP,那麼排序將作爲一個字符串(數字)工作 - 第一個字符是最重要的,然後是第二個等等。如果您的數字是:607,8和35,那麼CMP會將其分類爲:8,607,35。要將其排序爲數字,我們使用「< =>」方法,結果將爲:607,35,8

use strict; 

open my $FILE, '<', 'phonebook'; 
my @lines = <$FILE>; 

my @sorted = sort { 
       my @a = split(/\s+/,$a); 
       my @b = split(/\s+/,$b); 
       $b[1] <=> $a[1] } @lines; 

foreach my $item(@sorted) { 
    print "$item\n"; 
} 

close $FILE; 
+0

這並不回答問題。樣本輸入中每行的第二個字段(通過'split/\ s + /')是「Blenheim」,「Boop」,「Chevsky」和「Corder」。所有這些將使用'<=>'進行比較。 – hobbs 2012-12-02 06:04:25

相關問題