[英]In Perl, is there a built in way to compare two arrays for equality?
我有兩個字符串數組,我想比較相等:
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
是否有內置的方法來比較數組,就像標量一樣? 我試過了:
if (@array1 == @array2) {...}
但它只是在標量上下文中評估每個數組,因此比較了每個數組的長度。
我可以使用自己的函數來完成它,但它看起來像是一個低級操作,應該有一個內置的方法來做它。 在那兒?
編輯:遺憾的是,我無法訪問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 :
在內部,比較器通過使用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% --
我沒有看內存利用率。
還有Test :: More的is_deeply()函數,它也會精確顯示結構的不同之處,或者Test :: Deep的eq_deeply(),它不需要測試工具(只返回true或false)。
不是內置的,但有Array :: Compare 。
這是Perl核心遺漏的操作之一,因為我認為這是教學的原因 - 也就是說,如果你試圖這樣做,可能會出現問題。 我認為,最具說明性的例子是缺少核心read_entire_file
函數; 基本上,在核心中提供該功能會讓人們認為這樣做是個好主意 ,相反,Perl的設計方式會輕輕地推動您逐行處理文件,這通常會更多有效率和其他更好的想法,但新手程序員很少習慣它,他們需要一些鼓勵去實現。
這同樣適用於:通過比較兩個數組,可能有更好的方法來確定你想要完成的決定。 不一定 ,但可能。 所以Perl正在推動你思考實現目標的其他方法。
Perl 5.10為您提供智能匹配運算符。
use 5.010;
if( @array1 ~~ @array2 )
{
say "The arrays are the same";
}
否則,正如你所說,你將有自己的頂級。
只要您使用perl 5.10或更高版本,就可以使用智能匹配運算符 。
if (@array1 ~~ @array2) {...}
更簡單的解決方案更快:
#!/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% --
這個問題已經變成了一個非常有用的資源。 ++用於基准和討論。
正如其他人所指出的,智能匹配功能存在問題,並且正在逐步淘汰其目前的形式。 有些替代方案“不太聰明”(因此避免了這些問題),而且體積小,速度相當快,並且沒有太多的非CORE依賴項。
你可以找到更多有關未來的歷史一些非常好的討論~~
看在一對夫婦的博客文章由@布賴恩d FOY和P5P郵件歸檔線程從2011年和2012從@rjbs。
比較數組可以簡單而有趣!
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可以更輕松地進行微調比較。
如果套管是唯一的區別,你可以簡單地使用:
if (lc "@array1" eq lc "@array2") {...}
而"@array1"
返回與join ( " ", @array1 )
相同join ( " ", @array1 )
如果順序和重復值無關緊要但只有值相等(即設置比較),則可以使用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 };
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我的意思是“有用”。
可以在標量上下文中使用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
}
是的,我可能只是讓Larry Wall哭了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.