2012-08-26 28 views
6

Quindi tutto ciò di cui ho bisogno è un modo semplice per dire se due array sono identici in perl. Ordine non importa, quindi sto cercando qualcosa di simile:Come posso verificare se due array contengono gli stessi elementi in Perl?

my @a = (1, 2, 3);

my @b = (2, 3, 1);

my @c = (1, 2, 4);

&identical(@a, @b) restituisce 1

&identical(@a, @c) restituisce 0

Grazie!

+3

Si consiglia di scegliere un nome migliore. Nessuno di questi array è in realtà identico per definizione (che include avere gli stessi elementi nello stesso ordine). Se non ti interessa l'ordine, il nome dovrebbe riflettere questo. – cHao

+2

Gli elementi possono apparire in un array più di una volta? – Zaid

risposta

5

Se stai usando Perl 5.10 o superiore (e se non si è, si dovrebbe eseguire l'aggiornamento), è possibile utilizzare il smart match operator:

use strict; 
use warnings; 

my @a = (1, 2, 3); 
my @b = (2, 3, 1); 
my @c = (1, 2, 4); 

#sort each of them (numerically) 
@a = sort { $a <=> $b } @a; 
@b = sort { $a <=> $b } @b; 
@c = sort { $a <=> $b } @c; 

if (@a ~~ @b) { 

    print "\@a and \@b are the same! (after sorting)\n"; 
} 
else { 

    print "nope\n"; 
} 

if (@a ~~ @c) { 

    print "\@a and \@c are the same! (after sorting)\n"; 
} 
else { 

    print "nope\n"; 
} 

Si potrebbe anche rotolare la propria funzione:

use strict; 
use warnings; 

my @a = (1, 2, 3); 
my @b = (2, 3, 1); 
my @c = (1, 2, 4); 

print same_elements(\@a, \@b) . "\n"; 
print same_elements(\@a, \@c) . "\n"; 

#arguments are two array references 
sub same_elements { 

    my $array_ref_1 = shift; 
    my $array_ref_2 = shift; 
    my @arr1 = @$array_ref_1; 
    my @arr2 = @$array_ref_2; 

    #If they are not the same length, we are done. 
    if(scalar(@arr1) != scalar(@arr2)) { 

     return 0; 
    } 

    #sort them! 
    @arr1 = sort { $a <=> $b } @arr1; 
    @arr2 = sort { $a <=> $b } @arr2; 

    foreach my $i(0 .. $#arr1) { 

     if ($arr1[$i] != $arr2[$i]) { 
      return 0; 
     } 
    } 
    return 1; 
} 
+0

Downvoter: cura di spiegare? –

6

È possibile calcolare i conteggi degli elementi in un hash. Avere un hash (element => count) ed eseguire il count up ogni volta che il primo array ha quell'elemento, e giù ogni volta che l'altro lo ha (o viceversa). Se i due array hanno tutti gli stessi elementi, ogni valore nella hash sarà 0.

sub have_same_elements { 
    my ($arr1, $arr2) = @_; 
    my %counts =(); 
    $counts{$_} += 1 foreach (@$arr1); 
    $counts{$_} -= 1 foreach (@$arr2); 
    return !(grep { $_ != 0 } values %counts); 
} 


$a_and_b_same = have_same_elements(\@a, \@b); # will be true 
$a_and_c_same = have_same_elements(\@a, \@c); # will be false 

(Si noti, questo potrebbe o non potrebbe funzionare con gli oggetti che fanno il loro in stringa. Chiavi hash non possono essere riferimenti quindi Perl stringifica i riferimenti man mano che li usi.Il suo stringificatore predefinito trasforma i riferimenti in qualcosa come ARRAY(0x12345678), il che rende i riferimenti distinti a meno che non siano allo stesso oggetto, ma se un oggetto esegue la propria stringa e non restituisce stringhe distinte per diversi riferimenti, probabilmente si romperà. Solo così lo saprai.)

3

Prima di tutto, dovrai riconsiderare la tua funzione.

identical(@a, @b); 

non passa due array alla funzione, ma passa un singolo array con tutti gli elementi in entrambi gli array in esso. E 'come se lei ha detto:

identical(1, 2, 3, 2, 3, 1); 

Affinché la funzione di lavorare, si dovrà passare references per gli array:

identical(\@a, \@b); 

direi a prototype la subroutine, ma questo è probabilmente ti causerà more problems che risolverà.

Se l'ordine non è importante, ordinare gli array prima di confrontarli. Si potrebbe anche essere in grado di barare ...

sub identical { 
    my $array_ref_1 = shift; 
    my $array_fef_2 = shift; 

    use Digest::SHA qw(sha1_hex); 

    if (ref($array_ref_1) ne "ARRAY") or (ref($array_ref_2) ne "ARRAY") { 
     return; #Error, need two array references 
    } 

    # Dereference Arrays 
    my @array_1 = @{$array_ref_1}; 
    my @array_2 = @{$array_ref_2}; 

    # Setup Arrays to be one big scalar 
    my $scalar_1 = join "\n", sort @array_1; 
    my $scalar_2 = join "\n", sort @array_2; 

    my $checksum_1 = sha1_hex $scalar_1; 
    my $checksum_2 = sha1_hex $scalar_2; 

    if ($checksum_1 eq $checksum_2) { 
    return 1; 
    } 
    else { 
    return 0_but_true; 

Alcune note:

  • ho potuto avere dereferenziazioni, uniti, generate il checksum, e ha fatto il confronto in una singola istruzione. Li ho fatti separatamente per rendere più chiaro quello che stavo facendo. A livello di codice, probabilmente non fa alcuna differenza.Perl ottimizzerà comunque il tutto. Vado sempre per chiarezza.
  • 0_but_true restituisce uno 0, ma allo stesso tempo restituisce un valore vero. In questo modo, puoi fare qualcosa come if (identical(\@A, \@B)) { per assicurarti che la funzione abbia funzionato. Quindi, puoi provare zero o uno.
  • Assicurati di testare i tuoi parametri. Ho usato la funzione ref per fare questo.
  • I truffato. Per prima cosa ho trasformato i due array ordinati in scalari. Quindi, ho usato il checksum sha1 per verificare che fossero uguali. Un checksum che utilizza la funzione sha1 dovrebbe essere abbastanza buono. È altamente improbabile che fallisca.

Il vero problema è quello se aveste array multi-allineati come questo:

@a = ("this", "that", "the\nother"); 
@b = ("this", "that\nthe", "other"); 

Utilizzando la join il modo in cui ho fatto causerebbe lo scalari risultanti siano uguali.

0

Credo che si potrebbe scrivere come questo in un modo che rende meno ipotesi sul tipo di input si sta trattando (basta passare il sub confronto appropriata):

use List::Util; 
sub identical { 
    my @this = @{ +shift }; 
    my @that = @{ +shift }; 
    my $cmp = shift // sub { shift eq shift }; 
    return '' unless @this == @that; 
    for my $idx (List::Util::shuffle keys @this) { 
    return '' unless $cmp->($this[$idx], $that[$idx]); 
    } 
    return 1; 
} 

che si comporta in questo modo:

0> identical([0..100], [0..100]) 
$res[0] = 1 

1> identical([0..100], ['0.0', 1..100]) 
$res[1] = '' 

2> identical([0..100], ['0.0', 1..100], sub {shift == shift}) 
$res[2] = 1 

3> identical(['0.66666666666', 0..10], [2/3, 0..10], sub {shift == shift}) 
$res[3] = '' 

4> identical(['0.66666666666', 0..10], [2/3, 0..10], sub {shift() - shift() < 1e-5}) 
$res[4] = 1 

# if you need this to be true check out https://stackoverflow.com/a/12127428/13992 
5> identical([0..100], [List::Util::shuffle(0..100)]) 
$res[5] = '' 
Problemi correlati