我有两个数组,需要检查其中一个数组的元素是否出现在另一个数组中。
除了嵌套循环,有没有更有效率的方法呢?由于每个数组都有几千个元素,并且需要经常运行该程序。
另一种方法是使用 Array::Utils
use Array::Utils qw(:all);
my @a = qw( a b c d );
my @b = qw( c d e f );
# symmetric difference
my @diff = array_diff(@a, @b);
# intersection
my @isect = intersect(@a, @b);
# unique union
my @unique = unique(@a, @b);
# check if arrays contain same members
if ( !array_diff(@a, @b) ) {
# do something
}
# get items from array @a that are not in array @b
my @minus = array_minus( @a, @b );
perlfaq4
来帮忙:
如何计算两个数组的差异?如何计算两个数组的交集?
使用哈希表。以下代码可完成这些操作及更多。假设在给定的数组中每个元素都是唯一的:
如果你正确声明变量,代码将更像以下内容:
@union = @intersection = @difference = (); %count = (); foreach $element (@array1, @array2) { $count{$element}++ } foreach $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element; }
my %count; for my $element (@array1, @array2) { $count{$element}++ } my ( @union, @intersection, @difference ); for my $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element; }
CPAN
文档而不是perldoc
文档? - Zaid您需要提供更多的上下文信息。有一些更高效的方法可以实现,包括:
从Perl中出来,使用shell命令(sort
+ comm
)
将一个数组映射成一个Perl哈希表,然后循环遍历另一个数组检查哈希表成员身份。这种方法具有线性复杂度("M+N" - 基本上只需对每个数组进行一次循环),而不是嵌套循环的"M*N"复杂度。
例如:
my %second = map {$_=>1} @second;
my @only_in_first = grep { !$second{$_} } @first;
# use a foreach loop with `last` instead of "grep"
# if you only want yes/no answer instead of full list
使用一个Perl模块来代替最后一个任务点(评论中提到了List::Compare)
如果数据量很大且需要经常重新比较,则可以基于元素添加时间戳进行操作。几千个元素并不是真正的大数据,但我最近需要比较100k大小的列表。
Arrays::Utils
,这使得代码看起来简洁明了,但它并没有在后台执行任何强大的魔法。下面是array_diffs
代码:sub array_diff(\@\@) {
my %e = map { $_ => undef } @{$_[1]};
return @{[ ( grep { (exists $e{$_}) ? ( delete $e{$_} ) : ( 1 ) } @{ $_[0] } ), keys %e ] };
}
Arrays::Utils
不是标准模块,你需要考虑是否值得安装和维护这个模块。否则,这与DVK的答案相当接近。@array1 = qw(1 1 2 2 3 3 4 4 5 5);
@array2 = qw(1 2 3 4 5);
这些数组相同吗?或者说它们是不同的?它们具有相同的值,但是@array1
中有重复值而@array2
没有。
那么这个呢?
@array1 = qw( 1 1 2 3 4 5 );
@array2 = qw( 1 1 2 3 4 5 );
Array::Utils::arrays_diff
不这么认为。这是因为Array::Utils
假定没有重复的条目。sub array_diff {
my @array1 = @{ shift() };
my @array2 = @{ shift() };
my %array1_hash;
my %array2_hash;
# Create a hash entry for each element in @array1
for my $element ( @array1 ) {
$array1_hash{$element} = @array1;
}
# Same for @array2: This time, use map instead of a loop
map { $array_2{$_} = 1 } @array2;
for my $entry ( @array2 ) {
if ( not $array1_hash{$entry} ) {
return 1; #Entry in @array2 but not @array1: Differ
}
}
if ( keys %array_hash1 != keys %array_hash2 ) {
return 1; #Arrays differ
}
else {
return 0; #Arrays contain the same elements
}
}
my %array1_hash;
my %array2_hash;
map { $array1_hash{$_} += 1 } @array1;
map { $array2_hash{$_} += 2 } @array2;
for my $key ( keys %array1_hash ) {
if ( not exists $array2_hash{$key}
or $array1_hash{$key} != $array2_hash{$key} ) {
return 1; #Arrays differ
}
}
只有在%array1_hash
中的所有条目与其对应的%array2_hash
中的所有条目匹配时,您才会退出for循环。现在,您需要展示%array2_hash
中的所有条目也与其在%array1_hash
中的条目相匹配,并且%array2_hash
没有更多的条目。幸运的是,我们可以像之前一样做到这一点:
if ( keys %array2_hash != keys %array1_hash ) {
return 1; #Arrays have a different number of keys: Don't match
}
else {
return; #Arrays have the same keys: They do match
}
#!/usr/bin/perl -w
use strict;
my @list1 = (1, 2, 3, 4, 5);
my @list2 = (2, 3, 4);
my %diff;
@diff{ @list1 } = undef;
delete @diff{ @list2 };
print "Index: $_ => \@x: $x[$_], \@y: $y[$_]\n"
for grep { $x[$_] != $y[$_] } 0 .. $#x;
...or...
foreach( 0 .. $#x ) {
print "Index: $_ => \@x: $x[$_], \@y: $y[$_]\n" if $x[$_] != $y[$_];
}
你选择哪种方式,有点取决于你更关心的是保留不同元素的索引列表还是只是逐一处理不匹配的元素。grep版本很方便获取不匹配列表。(原文链接)
n + n log n算法,如果每个数组中的元素都是唯一的(作为哈希键)
my %count = ();
foreach my $element (@array1, @array2) {
$count{$element}++;
}
my @difference = grep { $count{$_} == 1 } keys %count;
my @intersect = grep { $count{$_} == 2 } keys %count;
my @union = keys %count;
my %count = ();
foreach (@array1) {
$count{$_} = 1 ;
};
foreach (@array2) {
$count{$_} = 2 if $count{$_};
};
# N log N
if (grep { $_ == 1 } values %count) {
return 'Some element of array1 does not appears in array2'
} else {
return 'All elements of array1 are in array2'.
}
# N + N log N
my @a = (1,2,3);
my @b=(2,3,1);
print "Equal" if grep { $_ ~~ @b } @a == @b;
@b
中包含"1
",这段代码似乎会打印出"Equal"。也许可以尝试say "Equal" if grep { $_ ~~ @b } @a && @a == @b ;
...但是感觉还是有点靠不住。 - G. Cito#!/usr/local/bin/perl
use strict;
my $file1 = shift or die("need file1");
my $file2 = shift or die("need file2");;
my @file1lines = split/\n/,`cat $file1`;
my @file2lines = split/\n/,`cat $file2`;
my %lines;
foreach my $file1line(@file1lines){
$lines{$file1line}+=1;
}
foreach my $file2line(@file2lines){
$lines{$file2line}+=2;
}
while(my($key,$value)=each%lines){
if($value == 1){
print "$key is in only $file1\n";
}elsif($value == 2){
print "$key is in only $file2\n";
}elsif($value == 3){
print "$key is in both $file1 and $file2\n";
}
}
exit;
__END__
List::Compare
- 特别是底部的部分 "If You Like List::Compare, You'll Love ..."。听起来你可能想要寻找一些用C实现而不是纯Perl的东西。http://search.cpan.org/perldoc/List::Compare - Telemachus