2012-08-26 355 views
6

所以我需要的是一個簡單的方法來判斷兩個數組是否在Perl中是相同的。順序並不重要,所以我在尋找這樣的事情:如何檢查兩個數組是否在Perl中包含相同的元素?

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

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

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

&identical(@a, @b)返回1

&identical(@a, @c)返回0

謝謝!

+3

您可能想要選擇一個更好的名稱。根據定義,這些數組實際上沒有任何一個是相同的(它們包括具有相同順序的相同元素)。如果你不關心訂單,這個名字應該反映出來。 – cHao

+2

元素能否不止一次出現在數組中? – Zaid

回答

5

如果您使用Perl 5.10或更高(如果你不是,你真的應該升級),你可以使用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"; 
} 

你也可以推出自己的功能:

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:謹慎解釋? – 2012-11-26 01:03:05

6

您可以在散列中計算元素的數量。有一個(元素=>計數)散列,每當第一個數組有這個元素時,往上碰一個數,每當另一個有它時(或者反過來)。如果兩個陣列都相同的元素,在每個哈希值將是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 

(注意,這可能會或可能不會與自己做的字串對象。哈希鍵不能引用,所以Perl會在你使用引用的時候對字符串進行字符串處理,它的默認字符串變量引用類似ARRAY(0x12345678)的引用,除非它們對應於相同的東西,否則引用不同。參考文獻,這可能會中斷。只是你知道。)

3

首先,你將不得不重新思考你的功能。

identical(@a, @b); 

不傳遞兩個數組到該函數,而是傳遞一個單個數組與其中兩個數組中的所有元素。這是因爲如果你說:

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

爲了使您的功能工作,你就必須通過references到您的數組:

identical(\@a, \@b); 

我到prototype說你的子程序,但是這可能會讓你more problems,它會解決。

如果順序不重要,請在比較它們之前對數組進行排序。你甚至可以欺騙......

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; 

的幾個注意事項:

  • 我能有指針引用,加盟,產生的校驗和,並沒有在單個語句的比較。爲了更清楚我所做的事情,我分別做了他們。以編程方式,它可能沒有任何區別。無論如何,Perl會優化整個事情。我總是清晰起來。
  • 0_but_true返回0,但同時返回一個真值。這樣,您可以執行諸如if (identical(\@A, \@B)) {之類的操作,以確保該功能正常工作。然後,您可以測試零個或一個。
  • 務必測試您的參數。我用ref函數來做到這一點。
  • 被騙了。我首先將兩個排序後的數組轉換爲標量。然後,我使用sha1校驗和來驗證它們是否相同。使用sha1函數的校驗和應該很好。這是不太可能失敗。

真正的問題是,如果你有什麼多行的數組是這樣的:

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

使用join我做會導致生成的標量相等的方式。

0

我想你可以在一定程度上使得上那種你正在處理輸入的最低假設它寫像這樣(只是通過適當的比較分):

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; 
} 

其行爲就像這樣:

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] = '' 
相關問題