如何检查两个数组在Perl中是否包含相同的元素?

时间:2012-08-26 02:48:36

标签: arrays 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

谢谢!

4 个答案:

答案 0 :(得分:6)

你可以计算哈希中元素的计数。有一个(element => count)哈希值,每次第一个数组都有该元素时都会向上计数,而每次有另一个数据时都会向下计数(反之亦然)。如果两个数组具有所有相同的元素,则散列中的每个值都将为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)的引用,这使得引用变得明显,除非它们是相同的东西。但是如果一个对象做了自己的字符串化并且没有为不同的引用返回不同的字符串,那么这可能会破坏。只是你知道。)

答案 1 :(得分: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;
}

答案 2 :(得分: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,但同时返回true值。这样,您可以执行if ( identical( \@A, \@B ) ) {之类的操作以确保该功能正常工作。然后,您可以测试零或一。
  • 请务必测试您的参数。我使用ref函数来执行此操作。
  • 被骗了。我首先将两个排序的数组转换为标量。然后,我使用sha1校验和来验证它们是否相同。使用sha1函数的校验和应该非常好。它极不可能失败。

真正的问题是,如果你有这样的多线阵列:

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

使用join我的方式会导致生成的标量相等。

答案 3 :(得分: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] = ''