我有两套,
@ a=(2,5,3)@ B=(3,4,1), I want to get (2,5,4,1)
输出是换行符、空格还是逗号并不重要
我的想法是:
Intersection @ c=(2,5,3,4,1), union @ d=(3), result: @ c - @ d=(2,5,4,1)
有没有更优雅的解决方案(我不喜欢安装额外的模块(
当我注意到问题的前提时
我不喜欢安装额外的模块
我仍然想展示库的实用性。带集::标量
use warnings;
use strict;
use feature 'say';
use Set::Scalar;
my @ary1 = (2,5,3);
my @ary2 = (3,4,1);
# "want to get (2,5,4,1)"; like union - intersection
my $s1 = Set::Scalar->new(@ary1);
my $s2 = Set::Scalar->new(@ary2);
my $u = $s1->union($s2);
my $i = $s1->intersection($s2);
my $res = $u - $i;
say $res;
# Or just
my $symm_diff = $s1->symmetric_difference($s2);
say $symm_diff;
打印(1 2 4 5)
行两次。
这种特殊的集合运算被称为对称差分,库中也有它,如末尾所示。这个图书馆能做的还有很多;这只是基本的东西。
还有相关的Set::Object,具有不同的倾斜度,但也具有上述所有方法(包括symmetric_difference
(,用于直接满足此需求。
在列表操作库之外,我会提到list::Compare,它也直接解决了这个问题,并且有更多内容。
使用哈希是表示集合的最简单方法(键是集合的成员,值可以表示任何含义;通常使用undef
(。以下是一些代码(可能更紧凑(:
@a = (2,5,3);
@b = (3,4,1);
foreach $val ( @a ) { $d{$val} |= 1 };
foreach $val ( @b ) { $d{$val} |= 2 };
foreach $val ( keys(%d) ) {
push(@result, $val) if $d{$val} != 3;
}
print(join(",", @result));
我们使用%d
值中的两位来表示每个集合中的成员身份,并跳过两个集合中的值。
my %s;
$s{ $_ } |= 1 for @a; # Sets bit 0 for elements of set @a
$s{ $_ } |= 2 for @b; # Sets bit 1 for elements of set @b
my @union = keys %s; # One or two bits set
my @isect = grep { $s{ $_ } == 3 } keys %s; # Both bits set
my @symdiff = grep { $s{ $_ } != 3 } keys %s; # Only one bit set
如果你只想要对称的差异,这应该更快:
my %a; @a{ @a } = (); delete @a{ @b };
my %b; @b{ @b } = (); delete @b{ @a };
my @symdiff = ( keys( %a ), keys( %b ) );