2010-04-07 21 views
7

J'ai deux tableaux, @a et @b. Je veux faire une comparaison entre les éléments des deux tableaux.Comment puis-je comparer des tableaux dans Perl?

my @a = qw"abc def efg ghy klm ghn"; 
my @b = qw"def ghy jgk lom com klm"; 

Si un élément correspond, définissez un indicateur. Y a-t-il un moyen simple de le faire?

+4

Parce que votre question ne sait pas, vous obtenez deux types de réponses: (1) ceux qui chercher des correspondances par paires, comme '$ a [$ i] eq $ b [$ i]'; et (2) ceux qui recherchent une correspondance, par exemple '$ a [$ i] eq $ b [$ j]'. Quel est ton but? – FMc

+0

duplicata possible de http://stackoverflow.com/questions/1609467/in-perl-is-there-a-built-in-way-to-compare-two-arrays-for-equality –

+0

@Sinan: cette question vous marqué comme "duplicata possible" n'est pas le même que celui-ci. Il s'agit de comparer tous les éléments de deux tableaux, mais celui-ci consiste à trouver un élément commun. –

Répondre

9

Tout d'abord, vos 2 tableaux doivent être correctement écrit.

@a = ("abc","def","efg","ghy","klm","ghn"); 
@b = ("def","efg","ghy","klm","ghn","klm"); 

Deuxièmement, pour les tableaux arbitraires (par exemple des tableaux dont les éléments peuvent être des références à d'autres structures de données), vous pouvez utiliser Data::Compare.

Pour les tableaux dont les éléments sont scalaires, vous pouvez faire une comparaison en utilisant List::MoreUtilspairwise BLOCK ARRAY1 ARRAY2, où BLOCK est votre sous-programme de comparaison. Vous pouvez émuler pairwise (si vous n'avez pas Liste :: moreutils accès) via:

if (@a != @b) { 
    $equals = 0; 
} else { 
    $equals = 1; 
    foreach (my $i = 0; $i < @a; $i++) { 
     # Ideally, check for undef/value comparison here as well 
     if ($a[$i] != $b[$i]) { # use "ne" if elements are strings, not numbers 
           # Or you can use generic sub comparing 2 values 
      $equals = 0; 
      last; 
     } 
    } 
} 

post-scriptum Je ne suis pas sûr mais List::Compare peut toujours trier les listes. Je ne suis pas sûr s'il peut faire des comparaisons par paires.

+0

Vous utilisez trop scalaire à mon goût. –

+0

Les opérateurs de comparaison scalaires imposent un contexte scalaire sur leurs arguments. Donc, '@a == @ b' est la même chose que' scalar (@a) == scalaire (@b) 'et' $ i <@ a' est la même chose que '$ i

+0

C'était pour la lisibilité de la démo ... je n'utilise presque jamais scalar() dans le code de production, pour la simple raison que vous ne l'avez pas aimé ici. Si vous pensez que cela n'aide pas beaucoup, je peux l'éditer – DVK

1
my @a = qw' abc def efg ghy klm ghn '; 
my @b = qw' def ghy jgk lom com klm '; 

my $flag; 

foreach my $item(@a) { 
    $flag = @b~~$item ? 0 : 1; 
    last if !$flag; 
} 

Notez que vous aurez besoin de Perl 5.10, ou plus tard, d'utiliser le smart match operator (~~).

+0

Mike - cela fonctionnera-t-il avant Perl 5.10? – DVK

+0

@DVK, je ne sais pas.Je l'ai appris de l'apprentissage Perl.Je n'ai pas besoin de l'instruction 5.010, mais peut-être seulement après Perl 5.10 Je vais vérifier, je suis toujours un apprenti Perl, veuillez me corriger si quelque chose a mal tourné :) – Mike

+0

@DVK, le livre dit: "Opérateur intelligent de Perl 5.10". Je n'ai pas besoin d'utiliser l'instruction 5.010, je l'ai juste testé à nouveau et l'instruction 5.010 n'est pas nécessaire au moins avec ActivePerl 5.10.0 sur WinXP, mais je suppose que cela ne marchera pas. avant Perl 5.10. – Mike

1

De l'exigence selon laquelle 'si tous les matchs de l'élément', utilisez l'intersection des ensembles:

sub set{ 
    my %set = map { $_, undef }, @_; 
    return sort keys %set; 
} 
sub compare{ 
    my ($listA,$listB) = @_; 
    return ((set(@$listA)-set(@$listB)) > 0) 
} 
+0

Cette question est taguée "perl". Je ne veux pas vous déprécier sans vous avertir d'abord ... –

+0

Aucune question n'est entièrement spécifique à la langue. Je suis sûr que quelqu'un peut venir avec une version perl de celui-ci. Il illustre également le point général. –

+3

Oui, mais si un débutant voit votre réponse, dans ce fil perl-tagged, sans aucune notification que ce n'est pas une réponse Perl, et tape dans, puis se demande pourquoi cela ne fonctionne pas? La seule chose responsable à faire avec cette réponse est de la réduire. Pardon. –

2

C'est une façon:

use warnings; 
use strict; 
my @a = split /,/, "abc,def,efg,ghy,klm,ghn"; 
my @b = split /,/, "def,ghy,jgk,lom,com,klm"; 
my $flag = 0; 
my %a; 
@a{@a} = (1) x @a; 
for (@b) { 
    if ($a{$_}) { 
     $flag = 1; 
     last; 
    } 
} 
print "$flag\n"; 
+0

Pas le plus facile à lire mais je l'aime bien. Lorsque vous répondez à une question, j'aime expliquer plus de comportements décalés, donc nous apprenons au lieu de cookbooking bien. – HerbN

+0

quelqu'un peut-il expliquer "@a {@a} = (1) x @a;"? –

+0

il remplit les clés de '% a' avec les éléments de' @ a', et les valeurs sont toutes égales à 1. ceci exploite l'allocation de Perl de différents types de variables avec le même nom, la tranche de hachage et un tableau dans un contexte scalaire. cependant, ce code ne fonctionnera pas pour comparer des tableaux avec des valeurs non uniques – beasy

0

Brute force devrait faire l'affaire pour les petits un n:

my $flag = 0; 
foreach my $i (@a) { 
    foreach my $k (@b) { 
     if ($i eq $k) { 
      $flag = 1; 
      last; 
     } 
    } 
} 

Pour la rge n, utilisez une table de hachage:

my $flag = 0; 
my %aa  =(); 
    $aa{$_} = 1 foreach (@a); 
foreach my $i (@b) { 
    if ($aa{$i}) { 
     $flag = 1; 
     last; 
    } 
} 

Lorsqu'un grand n est |@a| + |@b| > ~1000 articles

4

Vérifiez pour créer une fonction Intersection, qui renvoie une liste d'éléments qui sont présents dans les deux listes. Votre valeur de retour dépend alors du nombre d'éléments dans la liste recoupée.

Vous pouvez facilement trouver sur le web la meilleure implémentation d'intersection pour Perl. Je me souviens de l'avoir cherché il y a quelques années.

Voici ce que j'ai trouvé:

 

my @array1 = (1, 2, 3); 
my @array2 = (2, 3, 4); 
my %original =(); 
my @isect =(); 

map { $original{$_} = 1 } @array1; 
@isect = grep { $original{$_} } @array2; 

0

à mon humble avis, vous devez utiliser List::MoreUtils::pairwise. Cependant, si pour une raison quelconque vous ne pouvez pas, alors le sous suivant retournera un 1 pour chaque index où la valeur dans le premier tableau compare égale à la valeur dans le deuxième tableau.Vous pouvez généraliser cette méthode autant que vous le souhaitez et passer votre propre comparateur si vous le souhaitez, mais à ce stade, l'installation de List::MoreUtils serait une utilisation plus productive de votre temps.

use strict; use warnings; 

my @a = qw(abc def ghi jkl); 
my @b = qw(abc dgh dlkfj jkl kjj lkm); 
my $map = which_ones_equal(\@a, \@b); 

print join(', ', @$map), "\n"; 

sub which_ones_equal { 
    my ($x, $y, $compare) = @_; 
    my $last = $#$x > $#$y ? $#$x : $#$y; 
    no warnings 'uninitialized'; 
    return [ map { 0 + ($x->[$_] eq $y->[$_]) } $[ .. $last ]; 
} 
0

Ceci est Perl. La solution 'évidente':

my @a = qw"abc def efg ghy klm ghn"; 
my @b = qw"def ghy jgk lom com klm"; 
print "arrays equal\n" 
    if @a == @b and join("\0", @a) eq join("\0", @b); 

étant donné que "\ 0" n'est pas dans @a.

Mais merci de confirmer qu'il n'y a pas d'autre solution générique que de rouler la vôtre.

0
my @a1 = qw|a b c d|; 
my @a2 = qw|b c d e|; 

for my $i (0..$#a1) { 
    say "element $i of array 1 was not found in array 2" 
     unless grep {$_ eq $a1[$i]} @a2 
} 
Questions connexes