【问题标题】:Most efficient process of matching array elements in Perl?Perl中匹配数组元素的最有效过程?
【发布时间】:2014-12-25 08:14:48
【问题描述】:

我有两个数组(Array1 和 Array2)。 Array1 包含大约 20,000 个非唯一元素。 Array2 包含大约 90,000 个唯一元素。我正在尝试计算 Array1 中也显示为 Array2 元素的元素数量。下面的 perl 脚本成功但缓慢地做到了这一点。是否有另一种计算 Array2 中存在的 Array1 元素数量的方法可能比这更快?

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

# This program counts the total number of elements in one array that exist in a separate array.

my $Original_Array_File = "U:/Perl/MasterArray.txt";

# Read in the master file into an array.
open my $file, '<', $Original_Array_File or die $!;
my @Array2 = <$file>;
close $file;

my $path = "C:/Files by Year/1993";
chdir($path) or die "Cant chdir to $path $!";

for my $new_file ( grep -f, glob('*.txt') ) {
    open my ($new_fh), '<', $new_file;
    my @Array1 = <$new_file>;
    my @matched_array_count ;

    foreach @Array1 {
         ++$matched_array_count if ($_ ~~ @Array2 ) ;                            
         } 

【问题讨论】:

  • 输入文件是什么样的 - 格式?行数?
  • 您应该首先使用Devel::NYTProf module 分析您的脚本,以找出它在代码中花费最多时间的位置。
  • 输入文件是 .txt 文件,大小在 200 KB 到 3 MB 之间。它们是叙述性讨论。我将它们分解成六个单词的序列。
  • 嗨 Rick,自从您添加了 MCVE 以来,我已投票重新提出您的问题,现在很清楚您的问题是什么。通常的方法是将一个文件加载到散列中,然后循环通过第二个文件,使用exists 检查散列中是否有与当前行匹配的键。确保chomp 两个文件中的每一行。另外,请注意,从 Perl 5.18.0 开始,The smartmatch family of features are now experimental,因此您不应使用 ~~
  • 如果我之前的评论不清楚,~~ 是您的性能问题的原因。您正在进行@Array1 * @Array2 比较,或者在您的情况下,总共大约 18 亿次。如果您使用我上面解释的方法,您将只进行@Array1 哈希查找,或约 20,000 次,这会明显更快(假设哈希适合内存)。

标签: arrays performance perl element matching


【解决方案1】:

您似乎想要比较许多文件。

您可以将其拆分为使用一个线程池,每个文件一个线程。或者,如果您的内存受到更多限制,您可以将其中一个数组拆分为多个部分,并将每个部分发送到不同的线程。

【讨论】:

    【解决方案2】:

    性能问题的主要来源是:

    foreach @Array1 {
        ++$matched_array_count if ($_ ~~ @Array2 );                            
    } 
    

    根据smartmatch documentation的行为

    $string ~~ @array
    

    就像

    grep { $string ~~ $_ } @array
    

    $string1 ~~ $string2
    

    就像

    $string1 eq $string2
    

    将这些与您的原始代码 sn-p 放在一起,我们得到如下内容:

    foreach my $string1 @Array1 {
        ++$matched_array_count if grep { $string1 eq $_ } @Array2;
    }
    

    换句话说,将@Array1 中的第一个元素与@Array2 中的每个元素 进行比较,然后将@Array1 中的第二个元素与 进行比较@Array2 中的每个元素,依此类推。这可以解决

    scalar @Array1 * scalar @Array2
    

    比较,或总计约 18 亿次。

    这在 Perl 中通常使用哈希来完成。进行单个哈希查找比搜索数组中的每个元素要快得多。基本算法是:

    1. 将您的“干草堆”(您在 in 中搜索的内容)加载到哈希中。
    2. 遍历您的“针”(您正在搜索的内容)并使用exists 查看哈希中是否有匹配的键。

    在您的特定情况下,您会将U:/Perl/MasterArray.txt 的内容加载到哈希中,然后执行

    scalar @Array1
    

    哈希查找,或约 20,000。这将比您现在拥有的要快得多。

    这是一个在 Linux 字典文件中搜索单词的示例:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use 5.010;
    
    my $file = '/usr/share/dict/linux.words';
    open my $fh, '<', $file or die "Failed to open '$file': $!";
    
    my %haystack = map { chomp; $_ => 1 } <$fh>;
    
    my $count;
    while (<DATA>) {
        chomp;
        $count++ if exists $haystack{$_};
    }
    
    say $count;
    
    __DATA__
    foo
    bar
    foobar
    fubar
    

    输出:

    3
    

    (显然,“foobar”是一个词。“FUBAR”是一个词,但小写的“fubar”不是。)

    智能匹配是实验性的

    您还应该知道,从 Perl 5.18.0 开始,the smartmatch family of features are now experimental

    智能匹配,在 v5.10.0 中添加并在 v5.10.1 中进行了重大修改,一直是投诉点。尽管它有很多有用的方式,但它也被证明对于 Perl 的用户和实现者来说是有问题的和令人困惑的。关于如何最好地解决这个问题,已经提出了许多建议。 很明显,smartmatch 几乎肯定会在未来发生变化或消失。不建议依赖其当前行为。 (已添加重点)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      • 2017-10-16
      相关资源
      最近更新 更多