所以我只需要一种简单的方法来判断两个数组在 perl 中是否相同。顺序无关紧要,所以我正在寻找这样的东西:
my @a = (1, 2, 3);
my @b = (2, 3, 1);
my @c = (1, 2, 4);
&identical(@a, @b)
返回 1
&identical(@a, @c)
返回 0
谢谢!
您可以在哈希中计算元素的计数。有一个 (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)
,这使得引用不同,除非它们' re to the same thingie.但是如果一个对象进行了自己的字符串化并且不为不同的引用返回不同的字符串,这可能会中断。就像你知道的那样。)
如果您正在使用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;
}
首先,您将不得不重新考虑您的功能。
identical(@a, @b);
不将两个数组传递给函数,而是传递一个数组,其中包含两个数组中的所有元素。好像你说:
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;
几点注意事项:
0_but_true
返回一个 0,但同时返回一个真值。这样,您可以执行类似if ( identical( \@A, \@B ) ) {
的操作来确保该功能正常工作。然后,您可以测试零或一。真正的问题是如果你有这样的多行数组:
@a = ("this", "that", "the\nother");
@b = ("this", "that\nthe", "other");
使用join
我所做的方式会导致结果标量相等。
我想你可以这样写,对你正在处理的输入类型做出最少的假设(只需传递适当的比较子):
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] = ''