В Perl существует встроенный способ сравнения двух массивов для равенства?
У меня есть два массива строк, которые я хотел бы сравнить для равенства:
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
Есть ли встроенный способ сравнения массивов, например, для скаляров?
Я пробовал:
if (@array1 == @array2) {...}
но он просто оценил каждый массив в скалярном контексте и сравнил длину каждого массива.
Я могу свернуть свою собственную функцию, но это похоже на такую операцию на низком уровне, что должен быть встроенный способ сделать это. Есть?
Изменить: к сожалению, у меня нет доступа к 5.10+ или дополнительным компонентам.
Ответы
Ответ 1
Существует новый оператор smart match:
#!/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:
Внутри компаратор сравнивает два массива с помощью объединения, чтобы превратить оба массива в строки и сравнить строки с помощью 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% --
Я не рассматривал использование памяти.
Ответ 2
Здесь Test::More функция is_deeply(), которая также будет отображать, где структуры отличаются, или Test::Deep eq_deeply(), который не требует тестового жгута (и просто возвращает true или false).
Ответ 3
Не встроен, но есть Array:: Compare.
Это одна из операций, которые остались вне ядра Perl, поскольку я считаю, что это дидактические причины, то есть если вы пытаетесь это сделать, возможно, что-то не так. Наиболее наглядным примером этого, я думаю, является отсутствие функции ядра read_entire_file
; в основном, при условии, что функция в ядре заставит людей подумать, что это хорошая идея, но вместо этого Perl разработан таким образом, который мягко подталкивает вас к обработке файлов по очереди, что в целом намного больше эффективная и в противном случае лучшая идея, но начинающие программисты редко бывают довольны этим, и им нужно некоторое поощрение, чтобы добраться туда.
То же самое применимо и здесь: возможно, гораздо лучший способ сделать определение, которое вы пытаетесь выполнить, сравнивая два массива. Не обязательно, но, наверное. Поэтому Perl подталкивает вас думать о других способах достижения вашей цели.
Ответ 4
Пока вы используете perl 5.10 или новее, вы можете использовать оператор smart match.
if (@array1 ~~ @array2) {...}
Ответ 5
Perl 5.10 предоставляет вам оператор smart match.
use 5.010;
if( @array1 ~~ @array2 )
{
say "The arrays are the same";
}
В противном случае, как вы сказали, у вас будет собственный рулон.
Ответ 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% --
Ответ 7
Если оболочка является единственной разницей, вы можете просто использовать:
if (lc "@array1" eq lc "@array2") {...}
В то время как "@array1"
возвращает то же, что и join ( " ", @array1 )
Ответ 8
Этот вопрос превратился в очень полезный ресурс. ++ для тестов и обсуждения.
Как отмечали другие, функция интеллектуального сопоставления имела проблемы и постепенно прекращается в ее нынешней форме. Существуют альтернативы, которые "менее умны" (и, следовательно, избегают проблем), и которые являются небольшими, довольно быстрыми и не имеют слишком большого количества не зависимых от CORE зависимостей.
Вы можете найти ссылки на некоторые довольно хорошие дискуссии об истории будущего ~~
, посмотрев пару сообщений @brian d foy и p5p mail archive потоки с 2011 года и 2012 из @rjbs.
Сравнение массивов может быть простым и забавным!
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 может упростить сравнение.
Ответ 9
Если порядок и повторяющиеся значения не имеют значения, а только значения равенства (т.е. сравнения), вы можете использовать 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
.
Ответ 10
Для проверки равенства двух массивов попробуйте это.
В заданном коде, если% 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 };
Ответ 11
Можно использовать функцию grep в скалярном контексте (http://perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST)
(0 eq (grep {$ array1 [$ _] ne $array2 [$ _]} 0.. $# array1)), если $# array1 eq $# array2;
Е.И.В..
Ответ 12
Если единственным критерием является "они эквивалентны или нет?", а не более сложный вопрос: "являются ли они эквивалентными или нет, и если они отличаются друг от друга, как?" есть намного более быстрые/уродливые способы сделать это. Например, разбить все массивы на два скаляра и сравнить их.
Например
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
}
Да, я, вероятно, просто сделал плач Ларри Стена.