在Perl中,是否有内置的方法来比较两个数组的相等性?

时间:2009-10-22 19:33:22

标签: arrays perl compare match

我有两个字符串数组,我想比较相等:

my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");

是否有内置的比较数组的方法,就像标量一样? 我试过了:

if (@array1 == @array2) {...}

但它只是在标量上下文中评估每个数组,因此比较了每个数组的长度。

我可以使用自己的函数来完成它,但看起来这样的低级操作应该有一个内置的方法来完成它。有吗?

编辑:遗憾的是,我无法访问5.10+或可选组件。

15 个答案:

答案 0 :(得分:56)

有新的smart match operator

#!/usr/bin/perl

use 5.010;
use strict;
use warnings;

my @x = (1, 2, 3);
my @y = qw(1 2 3);

say "[@x] and [@y] match" if @x ~~ @y;

关于Array::Compare

  

比较器在内部通过使用join将两个数组转换为字符串并使用eq比较字符串来比较两个数组。

我想这是一个有效的方法,但只要我们使用字符串比较,我宁愿使用类似的东西:

#!/usr/bin/perl

use strict;
use warnings;

use List::AllUtils qw( each_arrayref );

my @x = qw(1 2 3);
my @y = (1, 2, 3);

print "[@x] and [@y] match\n" if elementwise_eq( \(@x, @y) );

sub elementwise_eq {
    my ($xref, $yref) = @_;
    return unless  @$xref == @$yref;

    my $it = each_arrayref($xref, $yref);
    while ( my ($x, $y) = $it->() ) {
        return unless $x eq $y;
    }
    return 1;
}

如果您要比较的数组很大,加入它们将会做很多工作并消耗大量内存,而不是逐个比较每个元素。

更新:当然,应该测试这样的陈述。简单的基准:

#!/usr/bin/perl

use strict;
use warnings;

use Array::Compare;
use Benchmark qw( cmpthese );
use List::AllUtils qw( each_arrayref );

my @x = 1 .. 1_000;
my @y = map { "$_" } 1 .. 1_000;

my $comp = Array::Compare->new;

cmpthese -5, {
    iterator => sub { my $r = elementwise_eq(\(@x, @y)) },
    array_comp => sub { my $r = $comp->compare(\(@x, @y)) },
};

这是最糟糕的情况,其中elementwise_eq必须遍历两个数组中的每个元素1_000次,并显示:

             Rate   iterator array_comp
iterator    246/s         --       -75%
array_comp 1002/s       308%         --

另一方面,最好的情况是:

my @x = map { rand } 1 .. 1_000;
my @y = map { rand } 1 .. 1_000;
              Rate array_comp   iterator
array_comp   919/s         --       -98%
iterator   52600/s      5622%         --
然而,

iterator表现下降得非常快:

my @x = 1 .. 20, map { rand } 1 .. 1_000;
my @y = 1 .. 20, map { rand } 1 .. 1_000;
              Rate   iterator array_comp
iterator   10014/s         --       -23%
array_comp 13071/s        31%         --

我没有看内存利用率。

答案 1 :(得分:22)

Test::More的is_deeply()函数,它也会显示结构的不同之处,或Test::Deep的eq_deeply(),它不需要测试工具(并且只返回是真是假。

答案 2 :(得分:14)

不是内置的,但有Array::Compare

这是Perl核心遗漏的操作之一,因为我认为这是教学的原因 - 也就是说,如果你想要这样做,可能会出现问题。我认为,最具说明性的例子是没有核心read_entire_file功能;基本上,在核心中提供该功能会让人们认为这是一个好主意这样做,但相反,Perl的设计方式可以轻轻地推动您处理文件行 - 在 - 时间,这通常效率更高,也是更好的主意,但是新手程序员很少习惯,他们需要一些鼓励来实现目标。

这同样适用于:通过比较两个数组,可能有更好的方法来确定您要完成的任务。不是必然,但可能。所以Perl正在推动你思考实现目标的其他方法。

答案 3 :(得分:9)

Perl 5.10为您提供智能匹配运算符。

use 5.010;

if( @array1 ~~ @array2 )
{
    say "The arrays are the same";
}

否则,正如你所说的那样,你将自己推出自己的产品。

答案 4 :(得分:8)

只要您使用的是perl 5.10或更新版本,就可以使用smart match operator

if (@array1 ~~ @array2) {...}

答案 5 :(得分:6)

更简单的解决方案更快:

#!/usr/bin/perl

use strict;
use warnings;

use Array::Compare;
use Benchmark qw( cmpthese );
use List::AllUtils qw( each_arrayref );

my @x = 1 .. 1_000;
my @y = map { "$_" } 1 .. 1_000;

my $comp = Array::Compare->new;

cmpthese -2, {
    iterator => sub { my $r = elementwise_eq(\(@x, @y)) },
    my_comp => sub { my $r = my_comp(\(@x, @y)) },
    array_comp => sub { my $r = $comp->compare(\(@x, @y)) },
};

@x = 1 .. 20, map { rand } 1 .. 1_000;
@y = 1 .. 20, map { rand } 1 .. 1_000;

cmpthese -2, {
    iterator => sub { my $r = elementwise_eq(\(@x, @y)) },
    my_comp => sub { my $r = my_comp(\(@x, @y)) },
    array_comp => sub { my $r = $comp->compare(\(@x, @y)) },
};

sub elementwise_eq {
    my ($xref, $yref) = @_;
    return unless  @$xref == @$yref;

    my $it = each_arrayref($xref, $yref);
    while ( my ($x, $y) = $it->() ) {
        return unless $x eq $y;
    }
    return 1;
}

sub my_comp {
    my ($xref, $yref) = @_;
    return unless  @$xref == @$yref;

    my $i;
    for my $e (@$xref) {
        return unless $e eq $yref->[$i++];
    }
    return 1;
}

导致perl 5, version 14, subversion 2 (v5.14.2) built for x86_64-linux-gnu-thread-multi

             Rate   iterator array_comp    my_comp
iterator   1544/s         --       -67%       -80%
array_comp 4697/s       204%         --       -41%
my_comp    7914/s       413%        68%         --
               Rate   iterator array_comp    my_comp
iterator    63846/s         --        -1%       -75%
array_comp  64246/s         1%         --       -75%
my_comp    252629/s       296%       293%         --

答案 6 :(得分:2)

这个问题已经变成了一个非常有用的资源。 ++用于基准和讨论。

正如其他人所指出的,智能匹配功能存在问题,并且正在逐步淘汰其目前的形式。有些替代品不太聪明" (因此避免了这些问题),这些问题很小,速度很快,并且没有太多非CORE依赖项。

通过查看@brian d foy的几个blog posts以及 p5p邮件存档,您可以找到关于~~未来历史的一些非常好的讨论的链接/ strong>来自@rjbs的帖子from 20112012

比较数组可以简单而有趣!

use v5.20;   
use match::smart; 
my @x = (1, 2, 3);       
my @y = qw(4 5 6);    
my @z = qw(4 5 6);   
say \@x |M| \@y ? "[\@x] and [\@y] match": "no match";  
say \@y |M| \@z ? "[\@y] and [\@z] match": "no match";

__END__                              
@y and @z match, @x and @y do not

...如果阵列很简单,特别有趣。但是数组可能是一个复杂的事情,有时您需要比较结果中的不同类型的信息。为此,Array::Compare可以更轻松地进行微调比较。

答案 7 :(得分:2)

为了检查两个数组的相等性,试试这个。 在给定的代码中,如果%eq_or_not具有任何值,则两个数组都不相等,否则它们是相等的。

my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");

my %eq_or_not;

@eq_or_not{ @array1 } = undef;
delete @eq_or_not{ @array2 };

答案 8 :(得分:1)

如果套管是唯一的区别,你可以简单地使用:

if (lc "@array1" eq lc "@array2") {...}

"@array1"返回与join ( " ", @array1 )

相同的内容

答案 9 :(得分:1)

如果订单和重复值无关紧要但只有值相等(即设置比较),则可以使用Set::Scalar

它会重载常见的运算符,例如==!=

my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");

if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}

或者,还有Algorithm::DiffList::Compare

答案 10 :(得分:1)

Data::Cmp是最近的另一个选择。 cmp_data()函数的操作类似于cmp运算符(有关cmp的用法,请参见perlop)。

示例:

use 5.10;
use Data::Cmp qw/cmp_data/;

my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
my @array3 = ("part1", "PART2", "part3", "part4");

# sample usage 
say "1 & 2 are different" if cmp_data(\@array1, \@array2) ;
sat "2 & 3 are the same" unless cmp_data(\@array2, \@array3) ;

还可以比较哈希值和更复杂的嵌套数据结构(在合理范围内)。对于没有非核心依赖性的单个模块,Data::Cmp相当“聪明” ;-) ... errm我的意思是“有用”。

答案 11 :(得分:1)

我的核心解决方案 List::Util::all

http {

    upstream loadbalancer {
        server app1:5000 weight=6;
        server app2:5000 weight=4;
    }

    server {
        location / {
            proxy_pass http://loadbalancer/;
        }
    }

}

events {
  
}

作为子程序:

use List::Util qw(all);

if (@array1 == @array2 && all { $array1[$_] eq $array2[$_] } 0..$#array1) {
    print "matched\n";
}

如果您想要自定义比较:

# call me like string_array_equals([@array1], [@array2])
sub string_array_equals {
    my ($array1, $array2) = @_;

    @$array1 == @$array2 and
    all { $array1->[$_] eq $array2->[$_] } 0..$#$array1;
}

此时,# call me like array_equals { $a eq $b } [@array1], [@array2] sub array_equals(&$$) { my ($compare, $array1, $array2) = @_; @$array1 == @$array2 and all { local $a = $array1->[$_]; local $b = $array2->[$_]; $compare->($a, $b); } 0..$#$array1; } 不会节省太多空间,您可以只执行 all

for

答案 12 :(得分:0)

可以在标量上下文中使用grep函数(http://perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST

(0 eq(grep {$ array1 [$ _] ne $ array2 [$ _]} 0 .. $#array1))if $#array1 eq $#array2;

HIH。

答案 13 :(得分:0)

if (join(",",sort @a) eq join(",",sort @b))

如果可以忽略性能问题,正如这里的线程中多次提到的

答案 14 :(得分:-2)

如果唯一的标准是"它们是否相同?",而不是更复杂的问题,"它们是否相同,如果它们不同,如​​何?&#34 ;有更快/更丑陋的方法来做到这一点。例如,将每个数组的整体粉碎成两个标量并进行比较。

例如

my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");

my $smash1 = join("", @array1);
my $smash2 = join("", @array2);

if ($smash1 eq $smash2)
{
  # equal
}
else
{
  #unequal
}

是的,我可能只是让Larry Wall哭了。