Question

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 vient d'évaluer chaque tableau dans un contexte scalaire, et compare donc la longueur de chaque tableau.

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

Modifier: Malheureusement, je n’ai pas accès à la version 5.10+ ni aux composants facultatifs.

Était-ce utile?

La solution

Il existe le nouvel opérateur de correspondance intelligente :

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

À propos de 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 consommera plus de mémoire que de simplement comparer chaque élément un à un.

Mise à jour : vous devez bien sûr 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 { "

Il existe le nouvel opérateur de correspondance intelligente :

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

À propos de 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 consommera plus de mémoire que de simplement comparer chaque élément un à un.

Mise à jour : vous devez bien sûr tester de telles déclarations. Repères simples:

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

C’est le pire scénario où elementwise_eq doit parcourir chaque élément des deux tableaux 1 fois sur 1000 et indique:

my @x = map { rand } 1 .. 1_000;
my @y = map { rand } 1 .. 1_000;

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

              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;
Les performances de

itérateur chutent assez rapidement, cependant:

              Rate   iterator array_comp
iterator   10014/s         --       -23%
array_comp 13071/s        31%         --
<*>

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

" } 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)) }, };

C’est le pire scénario où elementwise_eq doit parcourir chaque élément des deux tableaux 1 fois sur 1000 et indique:

<*>

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

<*> <*> Les performances de

itérateur chutent assez rapidement, cependant:

<*> <*>

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

Autres conseils

Il y a la la fonction is_deeply () de Test :: More , qui sera également affiche exactement où les structures diffèrent, ou Test eq_deeply () de : qui ne nécessite pas de test harnais (et renvoie simplement true ou false).

Pas intégré, mais il existe Array :: Compare .

C’est l’une des opérations laissées en dehors du noyau Perl pour ce que je considère être des raisons didactiques - c’est-à-dire que si vous essayez de le faire, il y a probablement quelque chose qui ne va pas. L’exemple le plus illustratif, à mon avis, est l’absence de fonction centrale read_entire_file ; En gros, fournir cette fonction au cœur inciterait les gens à penser que c'est une bonne idée de le faire, mais Perl a été conçu de manière à vous inciter doucement à traiter les fichiers en ligne. le temps, 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 d'accomplir 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 opérateur de match intelligent .

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 { "

Une solution plus simple est plus rapide:

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

Et le résultat est perl 5, version 14, sous-version 2 (v5.14.2) construite pour 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; }

Et le résultat est perl 5, version 14, sous-version 2 (v5.14.2) construite pour x86_64-linux-gnu-thread-multi :

<*>

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 évitez ainsi les problèmes) et qui sont petites, assez rapides et ne comportent 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 consultant quelques billets de blog par @brian d foy et l'archive p5p du courrier discussions à partir 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.

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

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

Alors que "@ array1" renvoie la même chose que join (""", @ array1)

Si l'ordre et les valeurs en double importent peu, mais uniquement l'égalité des valeurs (c.-à-d. la comparaison des ensembles), vous pouvez utiliser le 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) ) {...}

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

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épendance non essentielle, Data :: Cmp est plutôt "intelligent". ; -) ... errm, je veux dire "utile".

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

(0 eq (grep {$ tableau1 [$ _] ne $ tableau2 [$ _]} 0 .. $ # tableau1)) si $ # tableau1 eq $ # tableau2;

HIH.

Si le seul critère est "sont-ils équivalents ou non?", et non la question plus complexe, "sont-ils équivalents ou non, et s'ils diffèrent, comment?" il existe des moyens beaucoup plus rapides et plus laids de le faire. Par exemple, cassez l'intégralité de chaque tableau en deux scalaires et comparez-les.

Par exemple

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
}

Oui, je viens probablement de faire pleurer Larry Wall.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top