NAME
Set::Scalar - the basic set operations for Perl scalar/reference data
SYNOPSIS
use Set::Scalar;
or
use Set::Scalar qw(union intersection);
to import, for example, union
and intersection
to the current namespace. By default nothing is imported, the exportable routines are as_string union intersection symmetric_difference difference in compare equal disjoint proper_subset proper_superset subset superset, please see below for further documentation.
DESCRIPTION
Sets are created with new
. Lists as arguments for new
give normal sets, hash references (please see perlref
) give valued sets. The special sets, null set
or the none, empty, set, and the universal set
or the all are created with null
and universal
.
$a = Set::Scalar->new('a', 'b', 'c', 'd'); # set members
$b = Set::Scalar->new('c', 'd', 'e', 'f'); # set members
$c = Set::Scalar->new(qw(d e)); # set members
$d = Set::Scalar->new({'f', 12, 'g', 34}); # 'valued' set
$e = Set::Scalar->new($a, 'h', 'i'); # sets are recursive
$n = Set::Scalar->null; # the empty set
$u = Set::Scalar->universal; # the 'all' set
Valued sets are "added value" sets: normal sets have only their members but valued sets have one scalar/ref value per each of their member. See the discussion about values
and valued_members
for how to retrieve the values.
Set inversion or the not set is done with inverse
or the overloaded prefix operator -
.
$i = $a->inverse; # the 'not' set
$i = -$a; # or with the overloaded -
Displaying sets is done with as_string
or more commonly with the overloaded stringification "operator" "
.
print "a = ", $a->as_string, "\n";
print "b = $b\n"; # or with the overloaded "
print "c = $c\n";
print "d = $d\n";
print "e = ", $e, "\n";
print "i = $i\n";
print "n = $n\n";
print "u = $u\n";
NOTE: please do not try to display circular sets. Yes, circular sets can be built. Yes, trying to display them will cause infinite recursion.
The usual set operations are done with union
, intersection
, symmetric_difference
, and difference
, or with their overloaded infix operator counterparts, +
, *
, %
, and -
.
print "union(a,b) = ", Set::Scalar->union($a, $b), "\n";
print "a + b = ", $a + $b, "\n"; # or with the overloaded +
print "intersection(a,b) = ", Set::Scalar->intersection($a, $b), "\n";
print "a * b = ", $a * $b, "\n"; # or with the overloaded *
print "symmdiff(a,b) = ", Set::Scalar->symmetric_difference($a, $b), "\n";
print "a % b = ", $a % $b, "\n"; # or with the overloaded %
print "difference(a,b) = ", Set::Scalar->difference($a, $b), "\n";
print "a - b = ", $a - $b, "\n"; # or with the overloaded -
NOTE: the distributive laws (please see LAWS or t/laws.t in the Set::Scalar distribution) cannot always be satisfied. This is because in set algebra the whole universe (all the possible members of all the possible sets) is supposed to be defined beforehand BUT the set operations see only two sets at a time. This can cause the distributive laws
X + (Y * Z) == (X + Y) * (X + Z)
X * (Y + Z) == (X * Y) + (X * Z)
to fail because the +
and *
do not necessarily "see" all the members of the X
, Y
, Z
in time. Beware this effect especially when having simultaneously any two of the X, Y, Z, being identical in members except the other being inverted, or one the X, Y, Z, being the null set.
Modifying sets in-place
is done with insert
and delete
or their overload counterparts +=
and -=
. Testing for membership is done with in
.
print "a = $a\n";
$a->insert('x');
print "a' = $a\n";
print 'x is', $a->in('x') ? '' : ' not', " in a\n";
$a->delete('x');
print "a = $a\n";
print 'x is', $a->in('x') ? '' : ' not', " in a\n";
NOTE: set copying by =
is shallow. Sets are objects and the =
copies only the topmost level. That is, the copy is a reference to the original set.
$x = $a;
print "a = $a, x = $x, e = $e\n";
$a->insert('x');
print "a' = $a, x = $x, e = $e\n"; # also the 'copy' of a changes
$a->delete('x');
print "a' = $a, x = $x, e = $e\n";
For deep ("real") copying use copy
(or ->new($set)).
$y = $e->copy;
print "a = $a, y = $y, e = $e\n";
$a->insert('y');
# the (real, deep) copy does not change
print "a' = $a, y = $y, e = $e\n";
$a->delete('y');
print "a' = $a, y = $y, e = $e\n";
Testing sets is done with is_null
, is_universal
, is_inverted
, and is_valued
.
print 'a is', $a->is_null ? '' : ' not', " null\n";
print 'a is', $a->is_universal ? '' : ' not', " universal\n";
print 'a is', $a->is_inverted ? '' : ' not', " inverted\n";
print 'a is', $a->is_valued ? '' : ' not', " valued\n";
print 'd is', $d->is_null ? '' : ' not', " null\n";
print 'd is', $d->is_universal ? '' : ' not', " universal\n";
print 'd is', $a->is_inverted ? '' : ' not', " inverted\n";
print 'd is', $d->is_valued ? '' : ' not', " valued\n";
print 'i is', $i->is_null ? '' : ' not', " null\n";
print 'i is', $i->is_universal ? '' : ' not', " universal\n";
print 'i is', $i->is_inverted ? '' : ' not', " inverted\n";
print 'i is', $i->is_valued ? '' : ' not', " valued\n";
print 'n is', $n->is_null ? '' : ' not', " null\n";
print 'n is', $n->is_universal ? '' : ' not', " universal\n";
print 'n is', $n->is_inverted ? '' : ' not', " inverted\n";
print 'n is', $n->is_valued ? '' : ' not', " valued\n";
print 'u is', $u->is_null ? '' : ' not', " null\n";
print 'u is', $u->is_universal ? '' : ' not', " universal\n";
print 'u is', $u->is_inverted ? '' : ' not', " inverted\n";
print 'u is', $u->is_valued ? '' : ' not', " valued\n";
Comparing sets is done with
compare
equal
disjoint
intersect
proper_subset
proper_superset
subset
superset
or more commonly with their overloaded infix operator counterparts
<=>
==
!=
<>
<
>
<=
=>
NOTE: The compare
is a multivalued relational operator, not a binary (two-valued) one. It returns a string that is one of
==
!=
<>
<
>
<=
=>
The equal
, disjoint
, intersect
, proper_subset
, proper_superset
, subset
, and superset
, are binary (true or false) relational operators.
The difference between disjoint
and intersect
is that the former means completely disjoint, no common members at all, and the latter means partly disjoint, some common members, some not.
print "a <=> a = '", $a <=> $a, "'\n";
print "a == c\n" if ($a == $c);
print "b <=> c = '", $b <=> $c, "'\n";
print "c <=> b = '", $c <=> $b, "'\n";
print "b >= c\n" if ($b >= $c);
print "c < b\n" if ($c < $b);
print "a <=> c = '", $a <=> $c, "'\n";
print "a <=> d = '", $a <=> $d, "'\n";
NOTE: please do not try to "sort" sets based on the subset
and superset
relational operators. This will not work in general case because sets can have circular relationships. Circular sets will cause infinite recursion.
The set members can be accessed with members
. For the valued sets either the values can be accessed with values
as a list or both the members and the values with valued_members
as a hash. None of these returns the items in any particular order, the sets of Set::Scalar are unordered.
for $i ($a->members) { print "a: $i\n"; }
for $i (Set::Scalar->values($d)) { print "d: $i\n"; }
%d = $d->valued_members;
while (($k, $v) = each %d) { print "d: $k $v\n"; }
Sets can be grep
ed and map
ped.
%g = $a->grep(sub { $_[0] eq 'b' });
$g = Set::Scalar->new(keys %g);
print "g = $g\n";
%m = $d->map(sub { my ($k, $v, $d) = @_;
$k =~ tr/a-z/A-Z/;
$v *= $v;
$d = $k ne 'G';
($k, $v, $d); });
$m = Set::Scalar->new({ %m });
print "m = $m\n";
The power set (the set of all the possible subsets of a set) is generated with power_set
.
$p = $a->power_set;
print "p = $p\n";
Displaying sets can be fine-tuned either per set or by changing the global default display attributes using the display_attr
with two arguments. The display attributes can be examined using the display_attr
with one argument.
The display attributes are:
format
, a string which should contain magic sequences %s
which marks the place of the signedness (normal or inverted) of set, and %m
, which marks the place of the members of the set. The default is %s(%m)
inverse
, a string that tells how to mark an inverted set (the %s
in format
). The default is -
.
exists
, a string that tells how to mark an "existing" set. An "existing" set? It is a set that is not inverted, that is, a set that is "not not", (the %s
in format
). The default is ''.
member_separator
, a string that tells how to separate the members of the set, (the %m
in format
). The default is ' '.
value_style
, a string that tells how to display valued sets. Only two styles are defined: parallel
(the default) or serial
. The former means that the order is
m1 v1 m2 v2 ...
and the latter means that the order is
m1 m2 ... v1 v2 ...
value_indicator
, a string that tells how to separate the members from the values in the case of valued sets. In the parallel
style there are as many value_indicator
s shown as there are members (or values), in the serial
style only one value_indicator
is shown.
value_separator
, a string that tells how to separate the members and the values in case of serial
display of valued sets, (the %m
in format
). The default is ' '.
sort
, a name of a subroutine that tells how to order the members of the set. The default is '_DISPLAY_SORT' which sorts the members alphabetically. This is why the displayed form is something like this
(a b c d)
and not anything random (to be exact, in hash order). Sets do not have any particular order per se (please see the members
discussion).
print "format(a) = ", $a->display_attr('format'), "\n";
print "memsep(a) = ", $a->display_attr('member_separator'), "\n";
print "format(b) = ", $b->display_attr('format'), "\n";
print "memsep(b) = ", $b->display_attr('member_separator'), "\n";
# changing the per-set display attributes
$a->display_attr('format', '%s{%m}');
$a->display_attr('member_separator', ',');
print "a = $a, b = $b\n";
print "format(a) = ", $a->display_attr('format'), "\n";
print "memsep(a) = ", $a->display_attr('member_separator'), "\n";
print "format(b) = ", $b->display_attr('format'), "\n";
print "memsep(b) = ", $b->display_attr('member_separator'), "\n";
# changing the default display attributes
print "memsep = '", Set::Scalar->display_attr('member_separator'), "'\n";
Set::Scalar->display_attr('member_separator', ':');
print "memsep = '", Set::Scalar->display_attr('member_separator'), "'\n";
print "a = $a, b = $b\n";
Set::Scalar->display_attr('member_separator', ' ');
AUTHOR
Jarkko Hietaniemi, Jarkko.Hietaniemi@iki.fi