【问题标题】:Perl sort on numbersPerl 对数字进行排序
【发布时间】:2016-06-23 15:57:26
【问题描述】:

我有一些 perl 脚本来逐行处理一个文件(包含很多数字)。

文件内容(样本数据,前3个数字用空格隔开,然后隔开的是第3和第4个数字之间的制表符):

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;

这定义了两个散列:第一个散列用于排序,第二个散列用于在排序后删除重复项。

有没有更好的方法? 谢谢,

【问题讨论】:

  • 所以您根本不想保留第四个数字?删除重复项后你在做什么;特别是,您需要按某种顺序查看结果吗?
  • 我是否理解正确 - 您想对每行数字的重复项进行排序和删除,是吗? (所以有些行的数字会更少。)
  • 我根本不想保留第四个数字。并且没有重复编号,但排序后有重复行。例如,第 2 行和第 4 行在排序后将是相同的“2 8 9”。
  • @dellair 感谢您的澄清,我现在看到了那行。我已经更新了答案,以便它处理这种情况。请注意,我 split 制作 $n1 等的行用于测试——您的代码不需要它,删除它。如果需要更多/更好的解释,请告诉我。

标签: perl sorting hash


【解决方案1】:

更新另一种解决方案 - 可能是重复的行,而不是一行上的数字。


为了删除重复的行,最简单的方法是在一个数组中包含三个数字的所有已排序行。然后我们通过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 处理数组引用列表。它使用空格取消引用每个和joins 其元素,形成该行的字符串。它返回一个此类字符串的列表,每行一个。

  • 该列表由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) };

【讨论】:

    猜你喜欢
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 2011-07-06
    • 2012-01-21
    • 1970-01-01
    相关资源
    最近更新 更多