55

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

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

有没有像标量那样比较数组的内置方法?我试过:

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

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

我可以滚动我自己的函数来完成它,但它似乎是一个低级操作,应该有一个内置的方法来完成它。有没有?

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

4

15 回答 15

56

有新的智能匹配运算符

#!/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 次,它显示:

             速率迭代器 array_comp
迭代器 246/s -- -75%
array_comp 1002/s 308% --

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

my @x = map { rand } 1 .. 1_000;
my @y = map { rand } 1 .. 1_000;
              速率 array_comp 迭代器
array_comp 919/s -- -98%
迭代器 52600/s 5622% --

iterator然而,性能下降得很快:

my @x = 1 .. 20, map { rand } 1 .. 1_000;
my @y = 1 .. 20, map { rand } 1 .. 1_000;
              速率迭代器 array_comp
迭代器 10014/s -- -23%
array_comp 13071/s 31% --

我没有看内存利用率。

于 2009-10-22T19:46:09.917 回答
26

Test::More的 is_deeply() 函数,它也将准确显示结构不同的地方,或者Test::Deep的 eq_deeply(),它不需要测试工具(并且只返回 true 或 false)。

于 2009-10-22T19:58:52.977 回答
14

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

这是 Perl 核心中遗漏的操作之一,我认为这是出于教学原因——也就是说,如果您尝试这样做,可能会有问题。我认为最能说明这一点的例子是缺乏核心read_entire_file功能。基本上,在核心中提供该功能会导致人们认为这样做是个好主意,但是相反,Perl 的设计方式是轻轻推动您一次处理文件,这通常要多得多高效,否则是一个更好的主意,但新手程序员很少对此感到满意,他们需要一些鼓励才能到达那里。

这同样适用于这里:可能有更好的方法来通过比较两个数组来做出你想要完成的决定。不一定但可能。因此,Perl 正在推动您考虑其他实现目标的方法。

于 2009-10-22T19:35:31.560 回答
9

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

use 5.010;

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

否则,正如您所说,您将拥有自己的顶级滚动。

于 2009-10-22T19:45:24.990 回答
8

只要您使用 perl 5.10 或更高版本,就可以使用智能匹配运算符

if (@array1 ~~ @array2) {...}
于 2009-10-22T19:44:06.813 回答
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%         --
于 2011-12-01T01:48:36.303 回答
3

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

正如其他人指出的那样,智能匹配功能存在问题,并且正在以目前的形式逐步淘汰。有一些“不那么聪明”的替代品(因此可以避免这些问题),而且它们很小、相当快并且没有太多的非核心依赖项。

~~您可以通过查看@brian d foy 的几篇博客文章以及@rjbs的 2011 年2012 年的p5p 邮件存档线程,找到一些关于未来历史的很好讨论的链接。

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

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可以使微调比较更容易。

于 2014-12-16T17:33:00.127 回答
2

Data::Cmp是另一个最近的选择。该函数的操作与运算符cmp_data()类似cmp(参见用法)。perlopcmp

例子:

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 我的意思是“有用”。

于 2018-08-16T19:20:32.950 回答
1

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

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

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

于 2009-11-25T18:58:18.010 回答
1

如果顺序和重复值无关紧要,而只是值相等(即设置比较),则可以使用Set::Scalar.

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

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

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

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

于 2016-06-17T13:21:22.320 回答
1

为了检查两个数组的相等性,试试这个。在给定的代码中,如果 %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 };
于 2017-03-01T10:33:11.350 回答
1

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

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

# call me like array_equals { $a eq $b } [@array1], [@array2]
sub array_equals(&$$) {
    my ($compare, $array1, $array2) = @_;

    @$array1 == @$array2 or return 0;
    for (0..$#$array1) {
        local $a = $array1->[$_];
        local $b = $array2->[$_];
        $compare->($a, $b) or return 0;
    }
    1;
}
于 2021-02-26T11:32:51.823 回答
0

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

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

嗨。

于 2016-10-31T22:21:59.227 回答
0

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

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

于 2021-05-07T11:37:59.243 回答
-2

如果唯一的标准是“它们是否等效?”,而不是更复杂的问题,“它们是否等效,如果它们不同,如​​何?” 有更快/更丑的方法来做到这一点。例如,将每个数组的整体粉碎成两个标量并比较它们。

例如

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
}

是的,我可能只是让拉里沃尔哭了。

于 2011-11-30T22:51:43.307 回答