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

8
所以我需要一个简单的方法来判断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
4个回答

7
你可以用哈希表来计算元素的数量。建立一个(元素=>数量)的哈希表,在第一个数组中遇到该元素时增加其数量,在另一个数组中遇到时减少其数量(或反之亦然)。如果两个数组具有相同的所有元素,则哈希表中的每个值都将为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)的东西,这使得引用不同,除非它们指向相同的元素。但是,如果一个对象执行自己的字符串处理并且对不同的引用返回相同的字符串,这可能会破坏代码。只是让你知道。)

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

3

首先,您需要重新考虑您的函数。

 identical(@a, @b);

不要向函数传递两个数组,而是传递一个包含两个数组中所有元素的单个数组。这就好像你说:
identical(1, 2, 3, 2, 3, 1);

为了使您的函数正常工作,您需要将references传递给您的数组:
identical(\@a, \@b);

我建议你对子程序进行原型编程prototype,但这可能会导致比解决问题更多的问题
如果顺序不重要,请在比较数组之前对其进行排序。你甚至可以作弊...
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://dev59.com/qWfWa4cB1Zd3GeqPjbLk#12127428
5> identical([0..100], [List::Util::shuffle(0..100)])
$res[5] = ''

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接