2011-02-03 40 views
16

Quando provo seguenteCome sottrarre una matrice da una matrice?

#!/usr/bin/perl 

use strict; 
use warnings; 
use Data::Dumper; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 

print Dumper [grep {not @bl} @a]; 

ottengo un array vuoto. Mi sarei aspettato che @bl fosse sottratto da @a, quindi l'output era yellow purple pink.

Cosa c'è che non va qui?

+2

“sottrarre” non è il diritto parola qui. Quando ** fai ** trova la parola giusta, scoprirai che è quella che innesca un attacco hash pavloviano. – tchrist

risposta

33

è necessario attivare @bl in un hash per eseguire la differenza set:

my %in_bl = map {$_ => 1} @bl; 
my @diff = grep {not $in_bl{$_}} @a; 
+7

Questo è migliore della risposta faq per questa domanda - il faq mostra solo come calcolare la "differenza simmetrica" ​​tra due array – mob

+1

@mob: quindi mail brian con un aggiornamento suggerito. – tchrist

+6

In Perl 5.10 o successivo potresti scriverlo 'my @diff = grep {not $ _ ~~ @bl} @a;' –

4

@b1 viene valutata a true (si tratta di un array con un non-zero numero di elementi), quindi il test booleano nella vostra grep costrutto (not @b1) restituirà sempre false. grep filtra una matrice restituendo solo gli elementi per i quali il test booleano restituisce true.

È necessario verificare se $_ (l'elemento di matrice attualmente in considerazione) è in @bl oppure no. Un modo per farlo è quello di generare un hash temporanea utilizzando @bl come le chiavi, poi nel vostro check dichiarazione grep per la presenza di $_ nelle chiavi di hash:

#!/usr/bin/perl 

use strict; 
use warnings; 
use Data::Dumper; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 

# create a hash 
my %h; 

# nifty trick - use a hash slice to populate the 
# hash. The values are irrelevant so we'll use @bl 
# for those too 
@h{@bl} = @bl; 

print Dumper [grep {!exists $h{$_}} @a]; 
+1

Il popolamento dei valori di% h è eccessivo. Se usi 'exists', il popolamento con' @h {@bl} =() 'sarà perfetto e probabilmente più veloce. –

1

Un altro modo, utilizzando la funzione minus dal Acme::Tools modulo CPAN:

use strict; 
use warnings; 
use Data::Dumper; 
use Acme::Tools qw(minus); 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 
my @diff = minus(\@a, \@bl); 
print Dumper(\@diff); 

__END__ 

$VAR1 = [ 
      'yellow', 
      'purple', 
      'pink' 
     ]; 
2

Un'altra opzione utilizzando perl5i:

use perl5i::2; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 
my @diff = @a->diff(\@bl); 

say @diff->mo->perl; 
4

Poiché Perl 5.18.0 l'operatore smartmatch è considerato sperimentale: The smartmatch family of features are now experimental. Per questo motivo non utilizzerei più questa soluzione.

Un altro modo con lo SmartMatch operatore (se si dispone di perl versione 5.010 o superiore):

#!/usr/bin/env perl 
use warnings; 
use 5.012; 

my @bl = qw(red green blue); 
my @a = qw(green yellow purple blue pink); 

my @s = grep{ not $_ ~~ @bl } @a; 
say "@s"; # yellow purple pink 
+0

Non comporterà prestazioni 'O (n^2)', poiché l'operatore smartmatch corrisponde a ogni elemento di '@ bl' per ogni elemento in' @ a'? – Floegipoky

+0

@Floegipoky: il tuo commento mi sembra ragionevole. Con Perl 5 ho smesso di usare l'operatore smartmatch. Ho modificato la risposta. –

-1

Un altro modo è quello di utilizzare:

List::Compare CPAN module 
use List::Compare ; 
... 
my $compare_obj 
    = List::Compare->new(\@a , \@b1) ; 
@diff = $compare_obj->get_Lonly() ; 
...