web-dev-qa-db-fra.com

En Perl, existe-t-il un moyen intégré de comparer deux tableaux pour l’égalité?

J'ai deux tableaux de chaînes que je voudrais comparer pour l'égalité:

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

Existe-t-il un moyen intégré de comparer des tableaux comme il en existe pour les scalaires? J'ai essayé:

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

mais il a juste évalué chaque tableau dans un contexte scalaire, et a donc comparé la longueur de chaque tableau.

Je peux lancer ma propre fonction pour le faire, mais cela semble être une opération de bas niveau telle qu'il devrait y avoir un moyen intégré de le faire. Y a-t-il?

Edit: Malheureusement, je n’ai pas accès à la version 5.10+ ou aux composants optionnels.

49
Bill

Il y a le nouvel smart 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;

Concernant Array :: Compare :

En interne, le comparateur compare les deux tableaux en utilisant join pour les transformer en chaînes et en comparant les chaînes avec eq.

Je suppose que c'est une méthode valide, mais tant que nous utilisons des comparaisons de chaînes, je préférerais de loin utiliser quelque chose comme:

#!/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 les tableaux que vous comparez sont volumineux, les joindre vous demandera beaucoup de travail et de mémoire, au lieu de simplement comparer chaque élément un à un.

Mise à jour: Bien sûr, il convient de tester de telles déclarations. Repères 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)) },
};

Il s'agit du pire des cas où elementwise_eq doit parcourir chaque élément des deux tableaux 1 fois sur 1000 et affiche les éléments suivants:

 Évaluer l'itérateur array_comp 
 Iterator 246/s - -75% 
 Array_comp 1002/s 308% --

D'autre part, le meilleur scénario est:

my @x = map { Rand } 1 .. 1_000;
my @y = map { Rand } 1 .. 1_000;
 Évaluez array_comp iterator 
 Array_comp 919/s - -98% 
 Itérateur 52600/s 5622% --

iterator les performances chutent assez rapidement, cependant:

my @x = 1 .. 20, map { Rand } 1 .. 1_000;
my @y = 1 .. 20, map { Rand } 1 .. 1_000;
 Évaluez itérateur array_comp 
 Iterator 10014/s - -23% 
 Array_comp 13071/s 31% --

Je n'ai pas examiné l'utilisation de la mémoire.

56
Sinan Ünür

Il y a la fonction is_deeply () de Test :: More , qui affiche également exactement où les structures diffèrent, ou la fonction eq_deeply () de Test :: Deep , qui ne nécessite pas de test et retourne juste vrai ou faux).

21
Ether

Pas intégré, mais il y a Array :: Compare .

C’est l’une des opérations laissées en dehors du noyau Perl pour ce que je crois être des raisons didactiques: c’est-à-dire que si vous essayez de le faire, il ya probablement quelque chose qui ne va pas. L’exemple le plus illustratif, à mon avis, est l’absence de fonction de base read_entire_file; En gros, fournir cette fonction au cœur inciterait les gens à penser que c’est une bonne idée, mais Perl est conçu de manière à vous inciter doucement à traiter les fichiers de façon ponctuelle. , ce qui est généralement bien plus efficace et sinon une meilleure idée, mais les programmeurs débutants sont rarement à l'aise avec cela et ont besoin d'encouragement pour y arriver.

La même chose s’applique ici: il existe probablement un bien meilleur moyen de prendre la décision que vous essayez de réaliser en comparant deux tableaux. Pas nécessairement, mais probablement. Perl vous incite donc à réfléchir à d’autres moyens d’atteindre votre objectif.

14
chaos

Perl 5.10 vous donne l'opérateur de correspondance intelligente.

use 5.010;

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

Sinon, comme vous l'avez dit, vous aurez votre propre rôle.

8
David Harris

Tant que vous utilisez Perl 5.10 ou une version plus récente, vous pouvez utiliser l'opérateur correspondance intelligente

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

Une solution plus simple est plus rapide:

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

Et résulte 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%         --
6

Cette question est devenue une ressource très utile. ++ pour les repères et la discussion.

Comme d'autres l'ont souligné, la fonctionnalité de correspondance intelligente présentait des problèmes et est progressivement supprimée dans sa forme actuelle. Il existe des alternatives "moins intelligentes" (et donc évitant les problèmes), petites, assez rapides et ne comportant pas trop de dépendances non CORE.

Vous pouvez trouver des liens vers de très bonnes discussions sur l’histoire de l’avenir de ~~ en regardant quelques billets de blog de @brian d foy, et l’archive de courrier p5p threads de 2011 et 2012 de @rjbs.

Comparer des tableaux peut être simple et amusant! 

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

... particulièrement amusant si le tableau est simple. Mais un tableau peut être compliqué, et parfois vous voulez des informations différentes des résultats de la comparaison. Pour cela, Array :: Compare peut faciliter la comparaison.

2
G. Cito

Pour vérifier l’égalité de deux tableaux, essayez ceci . Dans un code donné, si% eq_or_not n’a aucune valeur, les deux tableaux ne sont pas égaux, sinon ils sont égaux.

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 };
2
Pradeep Gupta

Data::Cmp est une autre option récente. La fonction cmp_data() fonctionne de la même façon que l'opérateur cmp (voir perlop pour l'utilisation de cmp).

Exemple:

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

Il est également possible de comparer des hachages et des structures de données imbriquées plus complexes (dans des limites raisonnables). Pour un seul module sans dépendances non essentielles, Data::Cmp est plutôt "intelligent" ;-) ... errm je veux dire "utile".

1
G. Cito

Si le boîtier est la seule différence, vous pouvez simplement utiliser:

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

Considérant que "@array1" retourne la même chose que join ( " ", @array1 )

1
tempire

Si l'ordre et les valeurs en double importent peu, mais uniquement l'égalité des valeurs (c'est-à-dire la comparaison des ensembles), vous pouvez utiliser Set::Scalar .

Il surcharge les opérateurs communs tels que == ou !=.

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

if ( Set::Scalar->new(@array1) == Set::Scalar->new(@array2) ) {...}

Alternativement, il y a aussi Algorithm::Diff et List::Compare .

1
Archimedix

On pourrait utiliser la fonction grep dans un contexte scalaire ( http://perldoc.Perl.org/functions/grep.html#grep-BLOCK-LIST )

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

HIH.

0
f_v