arrays - En Perl, ¿existe una forma integrada de comparar dos matrices para la igualdad?




compare match (8)

Tengo dos matrices de cadenas que me gustaría comparar para la igualdad:

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

¿Existe una forma incorporada de comparar matrices como hay para escalares? Lo 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. ¿Esta ahí?

Editar: lamentablemente, no tengo acceso a los componentes 5.10+ o opcionales.


Esta pregunta se ha convertido en un recurso muy útil. ++ para los puntos de referencia y discusión.

Como otros han señalado, la característica 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ñas, bastante rápidas y no tienen demasiadas dependencias que no sean CORE.

Puede encontrar enlaces a algunas discusiones bastante buenas sobre la historia del futuro de ~~ al mirar un par de publicaciones de blog de @brian d foy, y los hilos de archivo de correo p5p 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 diferentes tipos de información de los resultados de la comparación. Para eso, Array::Compare puede facilitar la comparación ajustada.


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;

Respecto a Array::Compare :

Internamente, el comparador compara las dos matrices usando join para convertir ambas matrices en cadenas y comparar las cadenas usando eq .

Supongo que es un método válido, pero siempre que 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 en lugar de simplemente comparar cada elemento uno por uno.

Actualización: por supuesto, uno debe 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 { "$_" } 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 donde elementwise_eq tiene que atravesar cada elemento de ambas matrices 1_000 veces y muestra:

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

Por otro lado, el mejor escenario posible es:

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 rendimiento del iterator cae bastante rápido, sin embargo:

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

No miré la utilización de la memoria.


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 { "$_" } 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 el resultado en 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%         --

No incorporado, 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 core read_entire_file ; Básicamente, proporcionar esa función en el núcleo llevaría a la gente a pensar que es una buena idea hacerlo, pero en cambio, Perl está diseñado de una manera que te empuja suavemente hacia el procesamiento de archivos línea a línea, que generalmente es mucho más una idea eficiente y de otra manera mejor, pero los programadores noveles rara vez se sienten cómodos con ella y necesitan un poco de aliento para llegar allí.

Lo mismo se aplica aquí: es probable que haya una manera mucho mejor de hacer la determinación que está tratando de lograr mediante la comparación de dos matrices. No necesariamente , pero probablemente. Así que Perl te está empujando a pensar en otras formas de lograr tu objetivo.


Perl 5.10 le proporciona el operador de coincidencia inteligente.

use 5.010;

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

De lo contrario, como dijiste, tendrás el mejor rollo.



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 los valores de orden y duplicados no importan, sino solo valores de igualdad (es decir, establecer comparación), 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 .





match