Home > other >  Perl symmetric difference aka disjunctive union of two sets
Perl symmetric difference aka disjunctive union of two sets

Time:10-22

I have two sets,

@ a=(2,5,3)@ B=(3,4,1), I want to get (2,5,4,1)

Whether the output is a newline, a space, or a comma is not important

My idea is:

Intersection @ c=(2,5,3,4,1), union @ d=(3), result: @ c - @ d=(2,5,4,1)

Is there a more elegant solution (I don't like to install additional modules)

CodePudding user response:

Using a hash is the easiest way to represent a set (the keys are the members of the set, and the value can mean anything; often undef is used). Here's some code (could be more compact):

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

We are using two bits in the values of %d to represent membership in each set, and skipping values that are in both sets.

CodePudding user response:

While I duly note the question's premise

I don't like to install additional modules

I would still like to demonstrate the utility of libraries. With Set::Scalar

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;

Prints a (1 2 4 5) line, twice.

This particular set operation is called symmetric difference, and the library has it as well as shown at the end. There is a whole lot more that this library can do; this is just basic stuff.

There is also the related Set::Object, with a different slant, but also with all methods as above (including the symmetric_difference) for directly answering this need.

Out of list-manipulation libraries I'd mention List::Compare, which also directly resolves this question and has a lot more.

CodePudding user response:

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
  •  Tags:  
  • perl
  • Related