En Perl, ¿hay una forma integrada de comparar dos matrices para la igualdad?
Pregunta
Tengo dos conjuntos de cadenas que me gustaría comparar para la igualdad:
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
¿Hay una forma integrada de comparar matrices como la hay para escalares? Intenté:
if (@array1 == @array2) {...}
pero solo evaluó cada matriz en contexto escalar, y así comparó la longitud de cada matriz.
Puedo ejecutar mi propia función para hacerlo, pero parece una operación de tan bajo nivel que debería haber una forma integrada de hacerlo. ¿Hay?
Editar: lamentablemente, no tengo acceso a 5.10+ o componentes opcionales.
Solución
Existe el nuevo operador de coincidencia inteligente :
#!/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;
Con respecto a Array :: Compare :
Internamente, el comparador compara las dos matrices usando join para convertir ambas matrices en cadenas y comparando las cadenas usando
eq
.
Supongo que es un método válido, pero mientras usemos comparaciones de cadenas, preferiría usar algo como:
#!/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;
}
Si las matrices que está comparando son grandes, unirlas hará mucho trabajo y consumirá mucha memoria que solo comparar cada elemento uno por uno.
Actualización: Por supuesto, uno debería probar tales declaraciones. Puntos de referencia simples:
#!/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 { " Existe el nuevo operador de coincidencia inteligente :
#!/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;
Con respecto a Array :: Compare :
Internamente, el comparador compara las dos matrices usando join para convertir ambas matrices en cadenas y comparando las cadenas usando eq
.
Supongo que es un método válido, pero mientras usemos comparaciones de cadenas, preferiría usar algo como:
#!/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;
}
Si las matrices que está comparando son grandes, unirlas hará mucho trabajo y consumirá mucha memoria que solo comparar cada elemento uno por uno.
Actualización: Por supuesto, uno debería probar tales declaraciones. Puntos de referencia simples:
Rate iterator array_comp
iterator 246/s -- -75%
array_comp 1002/s 308% --
Este es el peor de los casos en el que elementwise_eq
tiene que pasar por todos y cada uno de los elementos en ambas matrices 1_000 veces y muestra:
my @x = map { rand } 1 .. 1_000;
my @y = map { rand } 1 .. 1_000;
Por otro lado, el mejor escenario es:
Rate array_comp iterator
array_comp 919/s -- -98%
iterator 52600/s 5622% --
my @x = 1 .. 20, map { rand } 1 .. 1_000;
my @y = 1 .. 20, map { rand } 1 .. 1_000;
Sin embargo, el rendimiento de iterator
cae bastante rápido:
Rate iterator array_comp
iterator 10014/s -- -23%
array_comp 13071/s 31% --
<*>
No miré la utilización de la memoria.
" } 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)) },
};
Este es el peor de los casos en el que elementwise_eq
tiene que pasar por todos y cada uno de los elementos en ambas matrices 1_000 veces y muestra:
Por otro lado, el mejor escenario es:
<*> <*> Sin embargo, el rendimiento de iterator
cae bastante rápido:
No miré la utilización de la memoria.
Otros consejos
Hay Test :: More la función is_deeply (), que también mostrar exactamente dónde difieren las estructuras, o Test :: Deep 's eq_deeply (), que no requiere un arnés de prueba (y solo devuelve verdadero o falso).
No está integrado, pero hay Array :: Compare .
Esta es una de las operaciones que queda fuera del núcleo de Perl por lo que creo que son razones didácticas, es decir, si estás tratando de hacerlo, probablemente haya algo mal. El ejemplo más ilustrativo de esto, creo, es la ausencia de una función central read_entire_file
; Básicamente, proporcionar esa función en el núcleo llevaría a las personas a pensar que es una buena idea hacer eso, pero en cambio, Perl está diseñado de una manera que lo empuja suavemente hacia el procesamiento de archivos en línea. tiempo, que generalmente es mucho más eficiente y, por lo demás, una mejor idea, pero los programadores novatos rara vez se sienten cómodos con él y necesitan un poco de aliento para llegar allí.
Lo mismo se aplica aquí: probablemente haya una forma mucho mejor de tomar la determinación que está tratando de lograr comparando dos matrices. No necesariamente , pero probablemente. Entonces, Perl te está empujando a pensar en otras formas de lograr tu objetivo.
Perl 5.10 le ofrece el operador de partido inteligente.
use 5.010;
if( @array1 ~~ @array2 )
{
say "The arrays are the same";
}
De lo contrario, como dijiste, tendrás tu propio top roll.
Mientras esté usando perl 5.10 o más reciente, puede usar operador de partido inteligente .
if (@array1 ~~ @array2) {...}
La solución más simple es más rápida:
#!/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 { " La solución más simple es más rápida:
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% --
Y da como resultado perl 5, versión 14, subversión 2 (v5.14.2) creado para x86_64-linux-gnu-thread-multi
:
<*>" } 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;
}
Y da como resultado perl 5, versión 14, subversión 2 (v5.14.2) creado para x86_64-linux-gnu-thread-multi
:
Esta pregunta se ha convertido en un recurso muy útil. ++ para los puntos de referencia y discusión.
Como otros han señalado, la función de coincidencia inteligente tenía problemas y se está eliminando gradualmente en su forma actual. Hay alternativas que son "menos inteligentes" (y así evitar los problemas) y que son pequeños, bastante rápidos y no tienen demasiadas dependencias que no sean CORE.
Puede encontrar enlaces a algunas discusiones bastante buenas sobre la historia del futuro de ~~
mirando un par de publicaciones de blog de @brian d foy y el p5p mail archive hilos de 2011 y 2012 de @rjbs .
¡Comparar matrices puede ser simple y divertido!
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
... especialmente divertido si la matriz es simple. Pero una matriz puede ser algo complicado y, a veces, desea obtener diferentes tipos de información de los resultados de la comparación. Para eso, Array :: Compare puede facilitar la comparación ajustada.
Si la carcasa es la única diferencia, simplemente puede usar:
if (lc "@array1" eq lc "@array2") {...}
Mientras que " @ array1 "
devuelve lo mismo que join (" " ;, @ array1)
Si el orden y los valores duplicados no importan sino solo la igualdad de valores (es decir, la comparación de conjuntos), puede usar
Sobrecarga operadores comunes como ==
o ! =
.
my @array1 = ("part1", "part2", "part3", "part4");
my @array2 = ("part1", "PART2", "part3", "part4");
if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}
Alternativamente, también hay Algorithm :: Diff
y List :: Compare
.
Para verificar la igualdad de dos matrices intente esto. En el código dado, si% eq_or_not tiene algún valor, ambas matrices no son iguales, de lo contrario son iguales.
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 };
Data :: Cmp
es otra opción reciente. La función cmp_data ()
funciona de manera similar al operador cmp
(consulte perlop
para uso cmp
).
Ejemplo:
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) ;
También es posible comparar hashes y estructuras de datos anidados más complicadas (dentro de lo razonable). Para un solo módulo sin dependencias no centrales, Data :: Cmp
es bastante "inteligente". ;-) ... errm me refiero a "útil".
Se podría usar la función grep en contexto escalar ( http: // perldoc.perl.org/functions/grep.html#grep-BLOCK-LIST )
(0 eq (grep {$ array1 [$ _] ne $ array2 [$ _]} 0 .. $ # array1)) if $ # array1 eq $ # array2;
HIH.
Si el único criterio es "¿son equivalentes o no?", y no la pregunta más compleja, "son equivalentes o no, y si difieren, ¿cómo?" Hay formas mucho más rápidas / feas de hacerlo. Por ejemplo, rompa la totalidad de cada matriz en dos escalares y compárelos.
Por ejemplo
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í, probablemente acabo de hacer llorar a Larry Wall.