8

したがって、必要なのは、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 を返します

ありがとう!

4

4 に答える 4

7

要素の数をハッシュで集計できます。(要素 => カウント) ハッシュを作成し、最初の配列にその要素が含まれるたびにカウントを増やし、もう一方の配列に含まれるたびにカウントを減らします (またはその逆)。2 つの配列の要素がすべて同じ場合、ハッシュのすべての値は 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)。ただし、オブジェクトが独自の文字列化を行い、異なる参照に対して異なる文字列を返さない場合、これはおそらく壊れます。

于 2012-08-26T03:35:29.250 に答える
6

Perl 5.10以上を使用している場合(そうでない場合は、実際にアップグレードする必要があります)、スマートマッチ演算子を使用できます。

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;
}
于 2012-08-26T03:05:29.593 に答える
3

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

 identical(@a, @b);

関数に 2 つの配列を渡すのではなく、両方の配列のすべての要素を含む 1 つの配列を渡します。まるであなたが言ったかのようです:

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

関数を機能させるには、配列への参照を渡す必要があります。

identical(\@a, \@b);

サブルーチンのプロトタイプを作成することをお勧めしますが、おそらくそれによって解決される問題がさらに発生するでしょう。

順序が重要でない場合は、比較する前に配列を並べ替えます。騙すこともできるかもしれません...

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;

いくつかのメモ:

  • 逆参照、結合、チェックサムの生成、および比較を 1 つのステートメントで行うことができました。自分が何をしているのかを明確にするために、それらを別々に行いました。プログラム的には、おそらく違いはありません。とにかく、Perl は全体を最適化します。私は常に明確さを求めます。
  • 0_but_true0 を返しますが、同時に真の値を返します。if ( identical( \@A, \@B ) ) {このようにして、関数が機能していることを確認するようなことができます。次に、ゼロか 1 かをテストできます。
  • パラメータを必ずテストしてください。これにはref関数を使用しました。
  • だましました。最初に、2 つの並べ替えられた配列をスカラーに変換しました。次に、sha1 チェックサムを使用して、それらが同じであることを確認しました。sha1関数を使用したチェックサムはかなり良いはずです。失敗する可能性は非常に低いです。

本当の問題は、次のような複数行の配列がある場合です。

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

私が行った方法を使用するjoinと、結果のスカラーが等しくなります。

于 2012-08-26T04:06:16.967 に答える