Is er in Perl een ingebouwde manier om twee arrays voor gelijkheid te vergelijken?

Ik heb twee reeksen strings die ik wil vergelijken voor gelijkheid:

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

Is er een ingebouwde manier om arrays te vergelijken zoals voor scalars?
Ik heb geprobeerd:

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

maar het evalueerde alleen elke array in scalaire context en vergeleek zo de lengte van elke array.

Ik kan mijn eigen functie gebruiken om het te doen, maar het lijkt zo’n low-level operatie dat er een ingebouwde manier zou moeten zijn om het te doen. Is er?

Bewerken: helaas heb ik geen toegang tot 5.10+ of optionele componenten.


Antwoord 1, autoriteit 100%

Er is de nieuwe slimme 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;

Met betrekking tot Array::Compare:

Intern vergelijkt de comparator de twee arrays door join te gebruiken om beide arrays in strings te veranderen en de strings te vergelijken met eq.

Ik denk dat dat een geldige methode is, maar zolang we stringvergelijkingen gebruiken, zou ik veel liever iets gebruiken als:

#!/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;
}

Als de arrays die u vergelijkt groot zijn, zal het samenvoegen ervan veel werk vergen en veel geheugen verbruiken dan elk element één voor één te vergelijken.

Update:Natuurlijk moet je zulke uitspraken testen. Eenvoudige benchmarks:

#!/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)) },
};

Dit is het worstcasescenario waarin elementwise_eqelk element in beide arrays 1_000 keer moet doorlopen en het volgende toont:

Beoordeel iterator array_comp
iterator 246/s -- -75%
array_comp 1002/s 308% --

Aan de andere kant is het beste scenario:

my @x = map { rand } 1 .. 1_000;
my @y = map { rand } 1 .. 1_000;
Beoordeel array_comp iterator
array_comp 919/s -- -98%
iterator 52600/s 5622% --

De prestaties van de

iteratornemen echter vrij snel af:

my @x = 1 .. 20, map { rand } 1 .. 1_000;
my @y = 1 .. 20, map { rand } 1 .. 1_000;
Beoordeel iterator array_comp
iterator 10014/s -- -23%
array_comp 13071/s 31% --

Ik heb niet naar geheugengebruik gekeken.


Antwoord 2, autoriteit 46%

Er is testen :: meer is_deeply () functie, wat ook zal zijn Toon precies waar de structuren verschillen, of test :: Deep eq_deeply (), die geen testharnas nodig heeft (en retourneert gewoon true of false).


Antwoord 3, Autoriteit 25%

Niet ingebouwd, maar er is array :: vergelijk .

Dit is een van de bewerkingen die buiten de PERL-kern is gelaten voor wat ik geloof zijn didactische redenen – dat wil zeggen, als je het probeert te doen, is er waarschijnlijk iets mis. Het meest illustratieve voorbeeld hiervan, denk ik, is de afwezigheid van een kern read_entire_filefunctie; Kortom, het verschaffen van die functie in de kern zou mensen leiden om te denken dat het een goed idee is om dat te doen, maar in plaats daarvan is Perl ontworpen op een manier die u voorzichtig nudgeert in de richting van het verwerken van bestanden Tijd, die over het algemeen veel efficiënter en anderszins een beter idee is, maar beginnende programmeurs zijn er zelden comfortabel mee en ze hebben wat aanmoediging nodig om er te komen.

Hetzelfde geldt hier: er is waarschijnlijk een veel betere manier om de vastberadenheid te maken die u probeert te bereiken door twee arrays te vergelijken. Niet noodzakelijkerwijs , maar waarschijnlijk. Dus Perl klopt je om na te denken over andere manieren om je doel te bereiken.


Antwoord 4, Autoriteit 16%

PERL 5.10 geeft u de Smart Match-operator.

use 5.010;
if( @array1 ~~ @array2 )
{
    say "The arrays are the same";
}

Anders, zoals u zei, heeft u de bovenste rol.


Antwoord 5, Autoriteit 14%

Zolang u PERL 5.10 of nieuwer gebruikt, kunt u de Smart Match-operator .

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

Antwoord 6, Autoriteit 11%

Eenvoudigere oplossing is sneller:

#!/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;
}

en resulteer in 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%         --

Antwoord 7, Autoriteit 5%

Deze vraag is veranderd in een zeer nuttige bron. ++ voor de benchmarks en discussie.

Zoals anderen al hebben opgemerkt, had de functie Smart Match problemen en wordt deze in zijn huidige vorm uitgefaseerd. Er zijn alternatieven die “minder slim” zijn (en dus de problemen vermijden) en die klein zijn, redelijk snel en niet te veel niet-CORE-afhankelijkheden hebben.

Je kunt links vinden naar behoorlijk goede discussies over de geschiedenis van de toekomst van ~~door een paar blogpostsdoor @brian d foy, en het p5p mailarchief discussies van 2011en 2012van @rjbs .

Het vergelijken van arrays kan eenvoudig en leuk zijn!

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

… vooral leuk als de array eenvoudig is. Maar een array kan ingewikkeld zijn en soms wil je verschillende soorten informatie uit de resultaten van de vergelijking. Daarom kan Array::Compareeen nauwkeurig afgestemde vergelijking vergemakkelijken.


Antwoord 8, autoriteit 4%

Data::Cmpis een andere recente optie. De functie cmp_data()werkt op dezelfde manier als de operator cmp(zie perlopvoor cmpgebruik).

Voorbeeld:

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) ;

Het is ook mogelijk om hashes en meer gecompliceerde geneste datastructuren te vergelijken (binnen redelijke grenzen). Voor een enkele module zonder niet-kernafhankelijkheden, is Data::Cmpbehoorlijk “slim” 😉… errm ik bedoel “nuttig”.


Antwoord 9, autoriteit 2%

Als de behuizing het enige verschil is, kun je gewoon het volgende gebruiken:

if (lc "@array1" eq lc "@array2") {...}

Terwijl "@array1"hetzelfde oplevert als join ( " ", @array1 )


Antwoord 10, autoriteit 2%

Als volgorde en dubbele waarden er niet toe doen, maar alleen gelijkheid (dwz vergelijking instellen), kunt u Set::Scalar.

Het overbelast veelvoorkomende operators zoals ==of !=.

my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}

Als alternatief zijn er ook Algorithm::Diffen List::Compare.


Antwoord 11, autoriteit 2%

Probeer dit om de gelijkheid van twee arrays te controleren.
Als in de gegeven code %eq_or_not een waarde heeft, zijn beide arrays niet gelijk, anders zijn ze gelijk.

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 };

Antwoord 12, autoriteit 2%

Mijn core-only oplossing met List::Util::all:

use List::Util qw(all);
if (@array1 == @array2 && all { $array1[$_] eq $array2[$_] } 0..$#array1) {
    print "matched\n";
}

Als subroutine:

# call me like string_array_equals([@array1], [@array2])
sub string_array_equals {
    my ($array1, $array2) = @_;
    @$array1 == @$array2 and
    all { $array1->[$_] eq $array2->[$_] } 0..$#$array1;
}

Als u een aangepaste vergelijking wilt:

# call me like array_equals { $a eq $b } [@array1], [@array2]
sub array_equals(&$$) {
    my ($compare, $array1, $array2) = @_;
    @$array1 == @$array2 and
    all {
        local $a = $array1->[$_];
        local $b = $array2->[$_];
        $compare->($a, $b);
    } 0..$#$array1;
}

Op dit moment bespaart allniet veel ruimte en kun je gewoon een fordoen:

# call me like array_equals { $a eq $b } [@array1], [@array2]
sub array_equals(&$$) {
    my ($compare, $array1, $array2) = @_;
    @$array1 == @$array2 or return 0;
    for (0..$#$array1) {
        local $a = $array1->[$_];
        local $b = $array2->[$_];
        $compare->($a, $b) or return 0;
    }
    1;
}

Antwoord 13

Je zou de grep-functie in scalaire context kunnen gebruiken (http:// perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST)

(0 eq (grep { $array1[ $_ ] ne $array2[ $_ ] } 0..$#array1)) als $#array1 eq $#array2;

HIH.


Antwoord 14

if (join(",",sort @a) eq join(",",sort @b))

als prestatieproblemen kunnen worden genegeerd, zoals meerdere keren in de discussies hier vermeld


Antwoord 15

Als het enige criterium is “zijn ze gelijkwaardig of niet?”, en niet de meer complexe vraag, “zijn ze gelijkwaardig of niet, en als ze verschillen, hoe?” er zijn veel snellere/lelijkere manieren om het te doen. Breek bijvoorbeeld het geheel van elke array in twee scalaire waarden en vergelijk die.

Bijvoorbeeld

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
}

Ja, waarschijnlijk heb ik Larry Wall zojuist aan het huilen gemaakt.

Other episodes