2016-06-19 36 views
0

我有一些perl腳本來逐行處理文件(包含大量數字)。Perl在數字上排序

文件內容(樣本數據,前3個數字被空間分隔,則單獨的是第三和第四數字之間標籤):

1 2 3 15 
2 9 8 30 
100 106 321 92 
9 8 2 59 
300 302 69 88 
.... 

腳本內容:

# snippet of script 
open(INF, "$infile") || die "Unable to open file $infile: $!\n"; 
@content = <INF>; 
close(INF); 

foreach $line (@content) { 
    # blah blah, script to handle math here 
    # Now the numbers are stored in separate variables 
    # $n1 stores the 1st number, i.e.: 1 
    # $n2 stores the 2nd number, i.e.: 2 
    # $n3 stores the 3rd number, i.e.: 3 
    # $n4 stores the 4th number, i.e.: 15 
    # Solution code to be inserted here 
} 

我想到:

  1. 對變量$ n1,$ n2,$ n3進行排序並以升序輸出。
  2. 在的foreach結束,擺脫重複的

我的方法:

# Insert below code to foreach 
$numbers{$n1} = 1; 
$numbers{$n2} = 1; 
$numbers{$n3} = 1; 
@keys = sort { $numbers{$b} <=> $numbers{$a} } keys %numbers; 
#push @numbers, "$keys[0] $keys[1] $keys[2]"; 
$numbers2{"$keys[0] $keys[1] $keys[2]"} = 1; 

這定義了兩個散列:第一散是排序,第二散對於排序後去除重複。

有沒有更好的方法? 謝謝,

+0

所以你不想保留第四個號碼?在擺脫重複之後,你在做什麼?特別是,你是否需要按照某種順序來查看結果? – ysth

+0

http://perlmaven.com/unique-values-in-an-array-in-perl – redbmk

+0

我理解這個權利 - 你想排序和刪除每行數字的重複,是嗎? (所以有些行最終會有更少的數字。) – zdim

回答

2

更新與另一種解決方案 - 這是行可能是重複的,而不是一行上的數字。


爲了去除重複行這是最簡單的,如果我們在一個陣列三個數字的所有排序線。然後我們通過運行uniq進行後處理。有(至少)兩種方式來做到這一點。

  • 將行存儲在數組中,每個行都是對具有三個數字的已排序數組的引用。然後爲了比較,在飛行中構建每個字符串。如果還有其他處理數字的地方,比如在數組中,這樣會更好。

  • 從每個已排序的行中構建一個字符串並將其存儲在一個數組中。然後比較容易。

下面我使用第一種方法,假設有其他處理的數字。

use warnings; 
use strict; 
use feature wq(say); 
use List::MoreUtils qw(uniq); 

my $file = 'sort_nums.txt'; 
my @content = do { 
    open my $fh, '<', $file or die "Can't open $file: $!"; 
    <$fh>; 
}; 

my @linerefs_all; 
foreach my $line (@content) { 
    # Calculations ... numbers stored in ($n1, $n2, $n3, $n4) 
    my ($n1, $n2, $n3) = split '\s+' $line; # FOR TESTING 
    # Add to @rlines a reference to the sorted array with first three 
    push @linerefs, [ sort { $a <=> $b } ($n1, $n2, $n3) ]; 
} 
# Remove dupes by comparing line-arrays as strings, then remake arrayrefs 
my @linerefs = map { [ split ] } uniq map { join ' ', @$_ } @linerefs_all; 
say "@$_" for @linerefs; 

文件sort_nums.txt在使用貼線,上面打印的代碼

 
1 2 3 
2 8 9 
100 106 321 
69 300 302 

說明後處理線,從右側讀取。

  • 右邊的map處理arrayrefs的列表。它將每個元素和一個空格解除引用,爲該行形成一個字符串。它返回一個這樣的字符串列表,每行一個。

  • 該列表被修剪爲uniq的副本,該列表本身返回一個列表,並被輸入左側的map

  • 在這map的塊中的每個字符串是由split(默認值)的白色空間爲(數字的上線)的列表,並且然後該基準被採取[ ]。因此,這個map返回數組的引用列表,每行一個,分配給@linerefs的內容。

然後打印。 如果對於一條語句來說這太過於困難,請將該過程分解成幾個步驟,以生成中間數組。或切換到上面的第二種方法。


戰後初期,假設每個行號可以重複

我把目標爲:排序三個變量,只保留那些獨特的,每一行。

use List::MoreUtils qw(uniq); 

foreach my $line (@content) { 
    # Calculations, numbers stored in ($n1, $n2, $n3, $n4) 
    my @nums = uniq sort { $a <=> $b } ($n1, $n2, $n3); 
    say "@nums"; 
} 

請記住,在這之後你不知道的$n1哪個(些),$n2$n3可能已被刪除。


如果由於某種原因,非核心模塊是不適合的,見this in perlfaq4例如,

my %seen =(); 
my @nums = sort { $a <=> $b } grep { ! $seen{$_}++ } ($n1, $n2, $n3); 

或者,如果你需要它不需要額外的哈希各地

my @nums = do { my %seen; sort { $a <=> $b } grep { !$seen{$_}++ } ($n1, $n2, $n3) };