Подтвердить что ты не робот

В Perl существует встроенный способ сравнения двух массивов для равенства?

У меня есть два массива строк, которые я хотел бы сравнить для равенства:

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

Есть ли встроенный способ сравнения массивов, например, для скаляров? Я пробовал:

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

но он просто оценил каждый массив в скалярном контексте и сравнил длину каждого массива.

Я могу свернуть свою собственную функцию, но это похоже на такую ​​операцию на низком уровне, что должен быть встроенный способ сделать это. Есть?

Изменить: к сожалению, у меня нет доступа к 5.10+ или дополнительным компонентам.

4b9b3361

Ответ 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 };

Ответ 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
}

Да, я, вероятно, просто сделал плач Ларри Стена.