Perl对称差,也就是两个集合的析取并集



我有两套,

@ 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 ) );

最新更新