In Perl esiste un modo integrato per confrontare due array per l’uguaglianza?

Ho due array di stringhe che vorrei confrontare per l’uguaglianza:

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

Esiste un modo integrato per confrontare gli array come per gli scalari? Provai:

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

ma ha appena valutato ogni array in un contesto scalare, e quindi ha confrontato la lunghezza di ciascun array.

Posso eseguire la mia funzione per farlo, ma sembra un’operazione di basso livello che ci dovrebbe essere un modo integrato per farlo. È lì?

Modifica: purtroppo, non ho accesso a 5.10+ o componenti opzionali.

C’è il nuovo operatore 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; 

Per quanto riguarda la matrice :: Confronta :

Internamente il comparatore confronta i due array usando join per trasformare entrambi gli array in stringhe e confrontare le stringhe usando eq .

Immagino che sia un metodo valido, ma fintanto che stiamo usando confronti tra stringhe, preferirei usare qualcosa come:

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

Se gli array che stai confrontando sono grandi, unirli farà un sacco di lavoro e consumerà molta memoria rispetto al confronto di ogni elemento uno per uno.

Aggiornamento: naturalmente, si dovrebbero testare tali dichiarazioni. Semplici parametri di riferimento:

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

Questo è lo scenario peggiore in cui elementwise_eq deve attraversare ogni singolo elemento in entrambe le matrici 1_000 volte e mostra:

              Rate iterator array_comp
 iteratore 246 / s - -75%
 array_comp 1002 / s 308% -

D’altra parte, lo scenario migliore è:

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

iterator prestazioni di iterator diminuiscono abbastanza rapidamente, tuttavia:

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

Non ho guardato l’utilizzo della memoria.

Esiste la funzione Test :: More di s_deeply (), che mostra anche esattamente dove le strutture differiscono, o Test :: Deep di eq_deeply (), che non richiede un’imbracatura di test (e restituisce solo vero o falso).

Non integrato, ma c’è Array :: Compare .

Questa è una delle operazioni lasciate fuori dal core Perl per quelle che credo siano ragioni didattiche – cioè, se si sta tentando di farlo, probabilmente c’è qualcosa di sbagliato. L’esempio più illustrativo di questo, penso, è l’assenza di una funzione read_entire_file base; Fondamentalmente, fornire quella funzione nel nucleo porterebbe le persone a pensare che sia una buona idea farlo, ma invece, Perl è progettato in un modo che ti spinge gentilmente verso l’elaborazione dei file alla volta, che è generalmente molto più efficiente e comunque un’idea migliore, ma i programmatori alle prime armi si trovano raramente a loro agio e hanno bisogno di incoraggiamento per arrivarci.

Lo stesso vale qui: c’è probabilmente un modo molto migliore per fare la determinazione che stai cercando di ottenere confrontando due array. Non necessariamente , ma probabilmente. Quindi Perl ti sta spingendo a pensare ad altri modi per raggiungere il tuo objective.

Perl 5.10 offre l’operatore di corrispondenza intelligente.

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

Altrimenti, come hai detto, avrai il meglio per te.

Finché si utilizza perl 5.10 o più recente, è ansible utilizzare l’ operatore smart match .

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

La soluzione più semplice è più veloce:

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

E risultato 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% -- 

Per verificare l’uguaglianza di due array, prova questo. Nel codice dato, se% eq_or_not ha un valore qualsiasi, entrambi gli array non sono uguali, altrimenti sono uguali.

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

Questa domanda è diventata una risorsa molto utile. ++ per i parametri di riferimento e la discussione.

Come altri hanno sottolineato, la funzionalità di corrispondenza intelligente ha avuto problemi ed è stata gradualmente eliminata nella sua forma attuale. Ci sono alternative che sono “meno intelligenti” (e quindi evitano i problemi) e che sono piccole, abbastanza veloci e non hanno troppe dipendenze non CORE.

  • Smart::Match
  • match::simple (and match::smart )
  • Scalar::In

Puoi trovare link a discussioni piuttosto buone sulla storia del futuro di ~~ guardando un paio di post di blog di @brian d foy e i thread di archivio di posta p5p del 2011 e 2012 di @rjbs.

Confrontare gli array può essere semplice e divertente!

 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 

… particolarmente divertente se l’array è semplice. Ma un array può essere una cosa complicata, ea volte si vogliono diversi tipi di informazioni dai risultati del confronto. Per questo, Array :: Compare può rendere più facile il raffronto sintonizzato.

Se l’involucro è l’unica differenza, puoi semplicemente usare:

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

Mentre "@array1" restituisce lo stesso di join ( " ", @array1 )

Se l’ordine e i valori duplicati non contano, ma solo l’uguaglianza dei valori (cioè il confronto degli insiemi), è ansible utilizzare Set::Scalar .

Sovraccarica operatori comuni come == o != .

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

In alternativa, c’è anche Algorithm::Diff e List::Compare .

Data::Cmp è un’altra opzione recente. La funzione cmp_data() opera in modo simile all’operatore cmp (vedere perlop per l’utilizzo di cmp ).

Esempio:

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

È anche ansible confrontare gli hash e le strutture di dati nidificati più complicati (entro limiti ragionevoli). Per un singolo modulo senza dipendenze non core, Data::Cmp è piuttosto “intelligente” 😉 … errm Intendo “utile”.

Si potrebbe usare la funzione grep in contesto scalare ( http://perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST )

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

HIH.

Se l’unico criterio è “sono equivalenti o no?”, E non la domanda più complessa, “sono equivalenti o meno, e se differiscono, come?” ci sono modi molto più veloci / più brutti per farlo. Ad esempio, distruggi l’intera gamma di ciascun array in due scalari e confrontali.

Per esempio

 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 } 

Sì, probabilmente ho appena fatto piangere Larry Wall.