В Perl есть встроенный способ сравнить два массива для равенства?
у меня есть два массива строк, которые я хотел бы сравнить для равенства:
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
есть ли встроенный способ сравнения массивов, как для скаляров? Я попробовал:
if (@array1 == @array2) {...}
но он просто оценивал каждый массив в скалярном контексте и поэтому сравнивал длину каждого массива.
Я могу свернуть свою собственную функцию, чтобы сделать это, но это кажется такой низкоуровневой операцией, что должен быть встроенный способ сделать это. Есть?
Edit: к сожалению, я не имеют доступа к 5.10+ или дополнительным компонентам.
13 ответов
новая 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;
внутренне компаратор сравнивает два массива с помощью 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 ' s eq_deeply (), который не требует тестовой проводки (и просто возвращает true или false).
Не встроенный, но есть Array:: Compare.
Это одна из операций, которая остается вне ядра Perl по тем, что я считаю дидактическими причинами , то есть, если вы пытаетесь это сделать, вероятно, что-то не так. Наиболее показательным примером этого, я думаю, является отсутствие ядра read_entire_file
функция; в основном, предоставление этой функции в ядре приведет к тому, что люди подумают, что это хорошая идея для этого, но вместо этого Perl разработанный таким образом, что мягко подталкивает вас к обработке файлов line-at-a-time, что, как правило, гораздо более эффективно и в остальном лучшая идея, но начинающие программисты редко комфортно с ним, и им нужно некоторое поощрение, чтобы добраться туда.
то же самое относится и здесь: вероятно, есть гораздо лучший способ сделать определение, которое вы пытаетесь выполнить, сравнивая два массива. Не обязательно, но вероятно. Так Perl подталкивает вас думать о других способы достижения цели.
Perl 5.10 дает вам оператор smart match.
use 5.010;
if( @array1 ~~ @array2 )
{
say "The arrays are the same";
}
в противном случае, как вы сказали, у вас будет свой собственный Top roll.
пока вы используете perl 5.10 или новее, вы можете использовать smart match operator.
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% --
для проверки равенства двух массивов попробовать это. В данном коде, если %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 };
этот вопрос превратился в очень полезный ресурс. ++ для критериев и обсуждения.
как указывали другие, функция smart match имела проблемы и постепенно исчезает в своем нынешнем виде. Есть альтернативы, которые "менее умны" (и поэтому избегают проблем), и которые небольшие, довольно быстрые и не имеют слишком много неосновных зависимостей.
вы можете найти ссылки на некоторые довольно хорошие дискуссии об истории будущего ~~
глядя на пару блога @brian d foy, и почтовый архив p5p темы от и 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 )
если порядок и повторяющиеся значения не имеют значения, а только равенство значений (т. е. сравнение наборов), вы можете использовать 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
.
Data::Cmp
еще один недавний вариант. The cmp_data()
функция работает аналогично cmp
оператора (см. perlop
на cmp
использования).
пример:
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)) если $#array1 eq $#array2;
взысканное.
Если единственным критерием является " эквивалентны ли они или нет?и не более сложный вопрос: "эквивалентны они или нет, и если они отличаются, то как?"есть гораздо более быстрые / уродливые способы сделать это. Например, разбейте весь массив на два скаляра и сравните их.
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
}