3

可能重复:
在 Perl 中,是否有内置方法来比较两个数组是否相等?

我需要将数组与应该返回的函数进行比较:

  • 如果在成对比较时所有元素都相等,则为 true
  • 如果所有元素都相等或成对比较时第一个数组中的元素未定义,则为 true
  • 在所有其他情况下为假

换句话说,如果子被称为“comp”:

@a = ('a', 'b', undef, 'c');
@b = ('a', 'b', 'f', 'c');
comp(@a, @b); # should return true
comp(@b, @a); # should return false

@a = ('a', 'b');
@b = ('a', 'b', 'f', 'c');
comp(@a, @b); # should return true

显而易见的解决方案是在两个数组之间进行成对比较,但我希望它比这更快,因为比较在一大组数组上运行多次,并且数组可能有很多元素。

另一方面,要比较的数组的内容(即:所有可能的@b)是预先确定的并且不会改变。数组的元素没有固定的长度,并且不能保证它们可能包含哪些字符(制表符、逗号,你可以命名它)。

有比成对比较更快的方法吗?智能匹配不会削减它,因为如果所有元素都相等,它会返回 true(因此如果一个是 undef,则不是)。

打包和进行按位比较可以成为一种策略吗?当我浏览 pack/unpack 和 vec 的文档时,它看起来很有希望,但我在那里有点超出了我的深度。

谢谢。

4

1 回答 1

2

Perl 可以在我的 Macbook 上在大约 100 毫秒内比较 10,000 个成对元素的列表,所以我要说的第一件事是分析你的代码以确保这确实是问题所在。

做一些基准测试,你可以做一些事情来加快速度。

  • 确保在第一次匹配失败时保释。

假设您有很多不匹配的比较,这将节省大量时间。

  • 预先检查数组的长度是否相同。

如果它们的数组长度不同,则它们永远不会匹配。比较它们的尺寸,如果它们不同,请尽早返回。这避免了需要在循环内一遍又一遍地检查这种情况。

  • 使用迭代器而不是 C 风格的 for 循环。

成对迭代你通常会做类似的事情,for( my $idx = 0; $idx <= $#a; $idx += 2 )但迭代数组比使用 C 风格的 for 循环更快。这是 Perl 的一个优化技巧,在优化的 C 中在 perl 中执行工作比在 Perl 代码中执行更有效。这会为您带来大约 20%-30% 的收益,具体取决于您如何对其进行微优化。

for my $mark (0..$#{$a}/2) {
    my $idx = $mark * 2;
    next if !defined $a->[$idx] || !defined $b->[$idx];
    return 0 if $a->[$idx] ne $b->[$idx] || $a->[$idx+1] ne $b->[$idx+1];
}
return 1;
  • 预先计算有趣的索引。

由于一组对是固定的,您可以生成定义了哪些对的索引。这使得迭代器更加简单和快速。

state $indexes = precompute_indexes($b);

for my $idx ( @$indexes ) {
    next if !defined $a->[$idx];
    return 0 if $a->[$idx] ne $b->[$idx] || $a->[$idx+1] ne $b->[$idx+1];
}

return 1;

没有空值,这是 40% 的性能提升。你得到的越多,固定集中的空值就越多。

use strict;
use warnings;
use v5.10;  # for state

# Compute the indexes of a list of pairs which are interesting for
# comparison: those with defined keys.
sub precompute_indexes {
    my $pairs = shift;

    die "Unbalanced pairs" if @$pairs % 2 != 0;

    my @indexes;
    for( my $idx = 0; $idx <= $#$pairs; $idx += 2 ) {
         push @indexes, $idx if defined $pairs->[$idx];
     }

    return \@indexes;
}

sub cmp_pairs_ignore_null_keys {
    my($a, $b) = @_;

    # state is like my but it will only evaluate once ever.
    # It acts like a cache which initializes the first time the
    # program is run.
    state $indexes = precompute_indexes($b);

    # If they don't have the same # of elements, they can never match.
    return 0 if @$a != @$b;

    for my $idx ( @$indexes ) {
        next if !defined $a->[$idx];
        return 0 if $a->[$idx] ne $b->[$idx] || $a->[$idx+1] ne $b->[$idx+1];
    }

    return 1;
}

我仍然相信在 SQL 中使用自连接会更好,但还没有解决。

于 2012-11-02T18:00:04.860 回答