Perl:排序数组的一部分

时间:2015-06-04 14:52:20

标签: arrays regex perl sorting

我有一个数组,每行中有许多字段,间距不同,如:

INDDUMMY   drawing2   139       30        1        0        0        0        0        0
RMDUMMY    drawing2   69        2         1        0        0        0        0        0  
PIMP       drawing    7         0         1444     718      437      0        0        0

我正在尝试按第3个字段中的数字对此数组进行排序,因此所需的输出应为:

PIMP       drawing    7         0         1444     718      437      0        0        0
RMDUMMY    drawing2   69        2         1        0        0        0        0        0
INDDUMMY   drawing2   139       30        1        0        0        0        0        0

我尝试在排序函数中使用正则表达式进行拆分,如:

@sortedListOfLayers = sort {
    split(m/\w+\s+(\d+)\s/gm,$a)
    cmp
    split(m/\w+\s+(\d+)\s/gm,$b)
}@listOfLayers;

但它无法正常工作。我怎么做那种排序?

2 个答案:

答案 0 :(得分:1)

您需要进一步扩展排序功能。我也不确定split是否按照您的想法运作。拆分基于分隔符将文本转换为数组。

我认为你的问题是你的正则表达式 - 感谢gm标志 - 与你认为它匹配的东西不匹配。我可能会略微区别对待它:

#!/usr/bin/perl
use strict;
use warnings;

my @array = <DATA>;

sub sort_third_num {
   my $a1 = (split ( ' ', $a ) )[2];
   my $b1 = (split ( ' ', $b )) [2];
   return $a1 <=> $b1;
}

print sort sort_third_num @array;

__DATA__
NDDUMMY   drawing2   139       30        1        0        0        0        0        0
RMDUMMY    drawing2   69        2         1        0        0        0        0        0  
PIMP       drawing    7         0         1444     718      437      0        0        0

例如,这就是诀窍。

如果您打算采用正则表达式方法:

sub sort_third_num {
    my ($a1) = $a =~ m/\s(\d+)/;
    my ($b1) = $b =~ m/\s(\d+)/;
    return $a1 <=> $b1;
}

不全局匹配意味着只返回第一个元素。并且只返回'whitespace-digits'的第一个匹配项。我们也用数字比较,而不是字符串。

答案 1 :(得分:0)

如果要对列表进行排序并且sort块中使用的操作很昂贵,常用的Perl惯用法是Schwartzian Transform:您将操作应用于每个列表元素并存储结果与原始元素一起排序,然后映射回原始格式。

经典教科书示例是使用昂贵的-s文件测试按大小对目录中的文件进行排序。一种天真的方法是

my @sorted = sort { -s $a <=> -s $b } @unsorted;

每次比较操作必须执行两次-s

使用Schwartzian变换,我们将文件名映射到数组引用列表中,每个引用都引用一个包含list元素及其大小的数组(每个文件只需确定一次),然后按文件大小排序,最后将数组引用映射回文件名。这一切都只需一步完成:

my @sorted =
    map $_->[0],                 # 3. map to file name
    sort { a$->[1] <=> b$->[1] } # 2. sort by size
    map [ $_, -s $_ ],           # 1. evaluate size once for each file
    @unsorted;

在您的情况下,问题是提取每个数组元素的第三个字段是多么昂贵。如有疑问,请measure比较不同的方法。对于几十个文件,文件大小示例中的加速比率大约为10倍!

适用于您的问题的Schwartzian变换看起来像这样:

my @sorted =
    map $_->[0],                         # 3. Map to original array
    sort { $a->[1] <=> $b->[1] }         # 2. Sort by third column
    map [ $_, ( split( ' ', $_ ) )[2] ], # 1. Use Sobrique's idea
    @array;

如果使用的操作非常昂贵,以至于您希望避免每个值执行多次,以防您拥有相同的数组元素,则可以按this question中所述缓存结果;这被称为Orcish Maneuver