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.
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.
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).
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.
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.
Tant que vous utilisez Perl 5.10 ou une version plus récente, vous pouvez utiliser l'opérateur correspondance intelligente .
if (@array1 ~~ @array2) {...}
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% --
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.
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 };
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".
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 )
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
.
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.