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.

¿Fue útil?

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 Set :: Scalar .

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top