2012-08-26 23 views
6

私が必要とするのは、2つの配列がperlで同じかどうかを調べる簡単な方法です。順序は重要ではありませんので、私はこのような何かを探しています:2つの配列に同じ要素が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:ケアを説明する? –

6

要素の数をハッシュで集計できます。最初の配列にその要素があるたびにカウントを増加させ、他の要素がそれを持つたびにカウントダウンします(またはその逆)。二つの配列はすべて同じ要素を持っている場合は、ハッシュのすべての値は

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 

0(これはあるいは自分自身の文字列化を行うオブジェクトでは動作しない場合があります、注意してくださいになります。ハッシュキーが参照することはできませんそのため、Perlは参照を使用するときに文字列を文字列に変換します。デフォルトのstringizerは、参照をARRAY(0x12345678)のように変えます。ただし、同じものでなければ参照を区別できます。これはたぶんあなたが知っている)。

3

まず、あなたの機能を再考する必要があります。

identical(@a, @b); 

2つの配列を関数に渡すのではなく、両方の配列のすべての要素を含む単一の配列を渡します。私はあなたのサブルーチンをprototypeすると言うだろう

identical(\@a, \@b); 

、それはです:

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

をあなたの機能が動作するためには、あなたの配列にreferencesに合格する必要があります:それはあなたが言っているかのようですおそらくあなたにそれが解決する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)) {のような機能を実行して、その関数が機能することを確認できます。次に、0または1をテストできます。
  • 必ずパラメータをテストしてください。私はこのためにref関数を使用しました。
  • I は、を詐称しています。私は最初に2つのソートされた配列をスカラに変換しました。次に、sha1チェックサムを使用して、それらが同じであることを確認しました。 sha1関数を使用するチェックサムはかなり良いはずです。失敗する可能性は非常に低いです。

本当の問題は、あなたがこのようなマルチ並ぶ配列だった場合です:join私が行った方法を使用して

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

を結果のスカラーが等しくなるように原因となります。

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] = '' 
関連する問題