在Perl中,是否有内置的方式来比较两个数组是否相等?
我有两个string数组,我想比较它们是否相等:
my @array1 = ("part1", "part2", "part3", "part4"); my @array2 = ("part1", "PART2", "part3", "part4");
有一种内置的方法来比较数组,比如标量吗? 我试过了:
if (@array1 == @array2) {...}
但它只是在标量上下文中评估每个数组,因此比较每个数组的长度。
我可以推出我自己的function来做,但似乎是这样一个低级别的操作,应该有一个内置的方式来做到这一点。 在那儿?
编辑:可悲的是,我没有访问5.10 +或可选组件。
有一个新的智能匹配算子 :
#!/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 :
在内部,比较器通过使用连接将两个数组转换为string并使用
eq
比较string来比较两个数组。
我想这是一个有效的方法,但只要我们使用string比较,我宁愿使用像这样的东西:
#!/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; }
如果你正在比较的数组很大,join它们将会做很多工作,并且消耗大量的内存,而不是仅仅比较每个元素。
更新:当然,应该testing这样的陈述。 简单的基准:
#!/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 iterator 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% -
我没有看内存利用率。
还有Test :: More的is_deeply()函数,它也会显示结构不同的地方,或者Test :: Deep的eq_deeply(),它不需要testing工具(只返回true或false)。
不是内置的,但有Array :: Compare 。
这是Perl核心所没有的操作之一,我认为这些操作是由于教学原因 – 也就是说,如果你正在尝试这样做,可能会出现错误。 这个最具说明性的例子,我认为是没有核心read_entire_file
函数; 基本上,在核心中提供这个function会让人们认为这样做是一个好主意 ,相反,Perl的devise方式是轻轻地推动你逐行处理文件,这通常要远远多于有效的,否则一个更好的主意,但新手程序员很less感到舒服,他们需要一些鼓励去那里。
这同样适用于这里:通过比较两个数组,可能有更好的方法来做出你想要完成的决定。 不一定 ,但可能。 所以Perl推动着你去思考完成你的目标的其他方式。
Perl 5.10为您提供了智能匹配运算符。
use 5.010; if( @array1 ~~ @array2 ) { say "The arrays are the same"; }
否则,正如你所说,你会有自己的顶级滚动。
只要你使用的是perl 5.10或更新版本,你可以使用智能匹配运算符 。
if (@array1 ~~ @array2) {...}
更简单的解决scheme更快:
#!/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% --
如果套pipe是唯一的区别,你可以简单地使用:
if (lc "@array1" eq lc "@array2") {...}
而"@array1"
返回与join ( " ", @array1 )
这个问题已经变成了一个非常有用的资源。 ++为基准和讨论。
正如其他人指出,智能匹配function存在问题,正在逐步淘汰。 还有一些“不太聪明”的select(因此避免了这些问题),并且规模小,速度相当快,并且没有太多的非CORE依赖关系。
-
Smart::Match
-
match::simple
(和match::smart
) -
Scalar::In
你可以通过@brian d foy查看一些博客文章 ,以及2011年和2012年 @ rjbs上的p5p邮件存档线索,find关于未来历史的一些相当好的讨论的链接。
比较数组可以简单而有趣!
use v5.20; use match::simple; my @x = (1, 2, 3); my @y = qw(1 2 3); say "[@x] and [@y] match" if @x |M| @y; __END__ [1 2 3] and [1 2 3] match
如果数组很简单,特别有趣。 但是一个数组可能是一个复杂的事情,有时候你需要从比较结果中得到不同的信息。 为此, Array :: Compare可以使调整的比较更容易。
如果顺序和重复值不重要,只是值相等(即设置比较),您可以使用Set::Scalar
。
它重载了常见的操作符,如==
或!=
。
my @array1 = ("part1", "part2", "part3", "part4"); my @array2 = ("part1", "PART2", "part3", "part4"); if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}
另外,还有Algorithm::Diff
和List::Compare
。
为了检查两个数组的相等性,试试这个。 在给定的代码中,如果%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 };
可以在标量上下文中使用grep函数( http://perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST )
(0 eq(grep {$ array1 [$ _] ne $ array2 [$ _]} 0 .. $#array1))if $#array1 eq $#array2;
HIH。
如果唯一的标准是“它们是不是等价的”,而不是更复杂的问题,“它们是否等价,如果它们不同,怎么办? 有更快/更丑陋的方法来做到这一点。 例如,将每个数组的整体粉碎成两个标量并进行比较。
例如
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 }
是的,我可能只是让拉里·沃尔哭了。