In mathematical set theory, it is axiomatic that the empty set -- the set containing no members -- exists. The abstract data type should include a name for the empty set:
the-empty-set, the set that has no members.
The axioms of the most widely accepted set theories also assert the existence of any set containing one or two elements. The abstract data type should provide constructors for such sets:
create-singleton
Input: member, a value.
Output: singleton, a set of which the members are of the same
type as member.
Preconditions: none.
Postconditions: member is a member of singleton
and singleton has no other members.
create-doubleton
Inputs: first-member and second-member, values of
the same type.
Output: doubleton, a set of which the members are of the same
type as first-member and second-member.
Preconditions: none.
Postconditions: first-member is a member of
doubleton, second-member is a member of
doubleton, and doubleton has no other members.
In some axiomatizations of set theory, there is a ``universal'' set that
contains every value. But, depending on the type of the elements,
it is not obvious that any such set exists, and in addition it may be
extremely difficult to implement such sets as the set of all values of type
Real or of type Natural (using bignums). We
shall avoid these problems by not positing a universal set.
The most fundamental operation on existing sets is the membership test -- determining whether a given value is an element of a given set:
member
Inputs: candidate, a value, and sett, a set of
which the members are of the same type as candidate.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if
candidate is a member of sett and false if
it is not.
It should be possible to form the union and intersection of any two sets:
union
Inputs: left-operand and right-operand, both
sets, with members of the same type.
Output: result, a set of which the members are of the same
type as those of left-operand and
right-operand.
Preconditions: none.
Postconditions: Every member of left-operand is a member of
result. Every member of right-operand is a
member of result. Every element of result is
either a member of left-operand or a member of
right-operand or both.
intersection
Inputs: left-operand and right-operand, both
sets, with members of the same type.
Output: result, a set of which the members are of the same
type as those of left-operand and
right-operand.
Preconditions: none.
Postconditions: Every member of result is a member of
left-operand. Every member of result is a member
of right-operand. Every value that is both a member of
left-operand and a member of right-operand is a
member of result.
Some axiomatizations of set theory provide for a complementation operation
constructs the set of all values that are not members of a given set.
Other theorists hold that this unbounded operation, like the universal set,
is incoherent and unnecessary. Moreover, it is often difficult to
implement in a satisfactory way. So in this abstract data type we shall
instead use the operations of relative complementation and sundering,
which return only subsets of a given set. Relative complementation gives
the set of all members of one set that are not also members of another set;
this is the ``set difference'' operation for which Pascal uses the
- operator. Sundering is a generalization of this idea to
give the set of all members of a set that meet some condition that is
expressed as a Boolean function.
relative-complement
Inputs: left-operand and right-operand, both
sets, with members of the same type.
Output: result, a set of which the members are of the same
type as those of left-operand and
right-operand.
Preconditions: none.
Postconditions: Every member of result is a member of
left-operand. No member of result is a member of
right-operand. Every value that is a member of
left-operand but not of right-operand is a member
of result.
sunder
Inputs: sett, a set, and test, an operation that
takes one input, a value of the same type as the members of
sett, and yields out output, a Boolean.
Output: result, a set of which the members are of the same
type as those of sett.
Preconditions: none.
Postconditions: Every member of result is a member of
sett. Applying test to any member of
result yields true; applying it to any member of
sett that is not a member of result yields
false.
The special cases of union and relative-complement in which the right operand is a singleton occur often enough to justify separate operations:
adjoin
Inputs: sett, a set, and adjunct, a value of the
same type as the members of sett.
Output: result, a set of which the members are of the same
type as those of sett.
Preconditions: none.
Postconditions: Every member of sett is a member of
result. adjunct is a member of
result. Every member of result, other than
adjunct, is a member of sett.
disjoin
Inputs: sett, a set, and disjunct, a value of the
same type as the members of sett.
Output: result, a set of which the members are of the same
type as those of sett.
Preconditions: none.
Postconditions: Every member of result is a member of
sett. disjunct is not a member of
result. Every member of sett, other than
disjunct, is a member of result.
It is sometimes useful to determine how many members a set has:
cardinality
Input: operand, a set.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the number of members of
operand.
It is also useful to be able to test whether a set has any members at all:
empty
Input: operand, a set.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if operand
has no members and false if it has one or more.
There are two common relations between sets: equality, in the sense of having exactly the same members, and the subset relation:
equal
Inputs: left-operand and right-operand, both
sets, with members of the same type.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if every member of
left-operand is a member of right-operand and
vice versa, false otherwise.
subset
Inputs: left-operand and right-operand, both
sets, with members of the same type.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if every member of
left-operand is a member of right-operand,
false otherwise.
In set theory, the existence of the power set (set of subsets) of any given set is usually postulated, so the abstract data type should provide an operation to construct power sets:
power-set
Input: operand, a set.
Output: result, a set of which the members are sets of which
the members are of the same type as the members of operand.
Preconditions: none.
Postconditions: Every subset of operand is a member of
result, and every member of result is a subset
of operand.
Finally, we need some way of selecting and operating on the members of a given set. Since the members of a set are not ordered, the operation of selecting a member of a set is non-deterministic, in the sense that there's no way to count on getting a particular one; this is reflected in the weak postcondition of the following operation:
select
Input: operand, a set.
Outputs: selected, a member of the same type as the members
of operand, and remainder, a set of which the
members are again of that type.
Preconditions: operand is not the empty set.
Postconditions: selected is a member of
operand. Every member of remainder is a
member of operand. selected is not a member
of remainder. Every member of operand, except
selected, is a member of remainder.
Sometimes it is possible to avoid the trouble of taking a set apart with successive select operations by applying a higher-order operation on the set as a whole:
map
Inputs: sett, a set, and mapper, an operation
that takes one input, a value of the same type as the members of
sett, and yields one output.
Output: result, a set of which the members are of the type of
the output of mapper.
Preconditions: none.
Postconditions: The result of applying mapper to any member
of sett is a member of result. Every member of
result is the result of applying mapper to some
member of sett.
apply-to-each
Inputs: sett, a set, and applicand, an operation
that takes one input, a value of the same type as the members of
sett, and yields no outputs.
Outputs: none.
Preconditions: none.
Postconditions: applicand has been applied exactly once to
each member of sett.
every-member
Inputs: sett, a set, and test, an operation that
takes one input, a value of the type of the members of sett, and
yields one output, a Boolean.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is false if there is a member
e of sett such that applying test to
e yields false. Otherwise, result is
true.
some-member
Inputs: sett, a set, and test, an operation that
takes one input, a value of the same type as the members of
sett, and yields one output, a Boolean.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if there is a member
e of sett such that applying test to
e yields true. Otherwise, result is
false.
Sets module in HP Pascalset types in standard Pascal clearly fall considerably
short of supplying a satisfactory implementation of this abstract data
type. Members of Pascal sets must belong to some ordinal type, often (as
in HP Pascal) limited to a maximum cardinality of 256 members. The empty
set and singleton and doubleton constructors are available, the
in operator implements member, and +,
*, -, =, and <=
implement union, intersection, relative-complement,
equal, and subset, respectively. But Pascal does not provide
any version of cardinality, select, or any of the
higher-order operations, and implementing them in standard Pascal usually
means running through every possible value of the base type of the set to
determine whether it is in the set operand of the operation.
A better approach is to use a dynamically allocated structure of some sort.
As Walker points out in the textbook (page 233), the set operations are
much more efficient if one can assume that the elements of the data type,
though not regarded as ordered at the level of sets, can be arranged
inexpensively in some linear order at the level of implementation. I have
assumed that, in addition to an equality test EqualElement,
the Element module provides a Boolean function
PrecedesElement that determines whether one value of the type
precedes another in such a linear ordering (which may be entirely arbitrary
or conventional).
Walker suggests implementing sets as singly-linked lists, consistently
stored in ascending order (that is, so that PrecedesElement
would return true for each adjacent pair of elements). I've chosen
to illustrate instead what an implementation using binary search trees
looks like. Instead of importing the binary search tree operations from a
module, I wrote them into the implements section of the
Sets module, mainly because I needed to adapt a couple of them
to this particular application and to add a couple of operations that would
not usually be provided in a BinarySearchTrees module.
However, I've observed an implicit abstraction barrier: None of the set
operations refers in any way to the internal structure of binary search
trees, which are accessed only through the operations provided for them.
It would be straightforward to substitute a different implementation of
binary search trees (for instance, using self-balancing red-black
trees).
Because of Pascal's strong typing rules, I've provided only sets of type
Element in this package. This means that the map
operation is constrained to accept, as values for the mapper
operand, only functions that yield Element values. It also
implies that the powerset operation is not implemented.
Since the set type defined in this module is dynamically allocated, I supplied a deallocation procedure and an assignment operation that actually copies the set instead of simply duplicating the pointer.
Set is a reserved word in Pascal, so I've used
Congeries as the identifier denoting the type.
{ This module defines an interface for a set data type and implements it
for HP 9000 Series 700 workstations under HP-UX 9.x, using HP Pascal.
Programmer: John Stone, Grinnell College.
Original version: November 26, 1996
}
$heap_dispose on$
module Sets;
$search 'set-elements.o'$
import Elements;
export
type
Congeries = ^SetRecord; { an opaque type }
{ TheEmptySet is a constant denoting the set with no members. }
const
TheEmptySet = nil;
{ The CreateSingletonSet function constructs and returns a set containing
just the one element specified by its argument. }
function CreateSingletonSet (Member: Element): Congeries;
{ The CreateDoubletonSet function constructs and returns a set containing
each of the elements specified by its arguments, but nothing more. }
function CreateDoubletonSet (FirstMember, SecondMember: Element):
Congeries;
{ The MemberOfSet function determines whether a given element is actually
a member of a given set. }
function MemberOfSet (Candidate: Element; Sett: Congeries): Boolean;
{ The UnionOfSets function constructs and returns a set containing all
of the elements in the sets specified by its arguments, but nothing
more. }
function UnionOfSets (LeftOperand, RightOperand: Congeries): Congeries;
{ The IntersectionOfSets function constructs and returns a set containing
all of the elements common to the sets specified by its arguments, but
nothing more. }
function IntersectionOfSets (LeftOperand, RightOperand: Congeries):
Congeries;
{ The RelativeComplementOfSets function constructs and returns a set
containing all of the members of the set specified by its first
argument that are not also members of the set specified by its
second argument, but nothing more. }
function RelativeComplementOfSets (LeftOperand, RightOperand: Congeries):
Congeries;
{ The SunderSet function constructs and returns a set containing all of
the members of the set specified by its first argument that meet the
condition expressed by its second argument, in the sense that when
applied to such an element the second argument returns True. }
function SunderSet (Sett: Congeries;
function Test (Operand: Element): Boolean): Congeries;
{ The AdjoinToSet function constructs and returns a set containing all
of the members of the set specified by its first argument and also
the element specified by its second argument. If the latter is already
a member of the former, AdjoinToSet returns a freshly allocated copy
of the former. }
function AdjoinToSet (Sett: Congeries; Adjunct: Element): Congeries;
{ The DisjoinFromSet function constructs and returns a set containing all
of the members of the set specified by its first argument except for
the element specified by its second argument. If the latter is not
a member of the former, DisjoinFromSet returns a freshly allocated
copy of the former. }
function DisjoinFromSet (Sett: Congeries; Disjunct: Element): Congeries;
{ The CardinalityOfSet function determines and returns the number of
members in a given set. }
function CardinalityOfSet (Operand: Congeries): Integer;
{ The EmptySet function determines whether its argument has any members,
returning True if it has none and False if it has one or more. }
function EmptySet (Operand: Congeries): Boolean;
{ The EqualSets function determines whether its arguments are equal as
sets -- that is, whether every member of the first is a member of the
second, and vice versa. }
function EqualSets (LeftOperand, RightOperand: Congeries): Boolean;
{ The Subset function determines whether every member of the set
specified by its first argument is also a member of the set specified
by its second argument, returning True if so and False if not. }
function Subset (LeftOperand, RightOperand: Congeries): Boolean;
{ The SelectFromSet procedure chooses one member of a given non-empty
set and returns it through the variable-parameter Selected. It also
constructs a set containing all of the other members of the given set
and returns it through the variable-parameter Remainder. }
procedure SelectFromSet (Operand: Congeries; var Selected: Element;
var Remainder: Congeries);
{ The MapSet function applies a given function to each member of a given
set and returns a set containing all of the results. }
function MapSet (Sett: Congeries;
function Mapper (Operand: Element): Element): Congeries;
{ The ApplyToEachMemberOfSet procedure applies a given procedure to each
member of a given set. }
procedure ApplyToEachMemberOfSet (Sett: Congeries;
procedure Applicand (Operand: Element));
{ The EveryMemberOfSet function determines whether all of the members
of a given set have the property expressed by its second argument, in
the sense that applying the second argument to any such member would
yield True. }
function EveryMemberOfSet (Sett: Congeries;
function Test (Operand: Element): Boolean): Boolean;
{ The SomeMemberOfSet function determines whether at least one of the
members of a given set has the property expressed by its second
argument, in the sense that applying the second argument to such a
member would yield True. }
function SomeMemberOfSet (Sett: Congeries;
function Test (Operand: Element): Boolean): Boolean;
{ The AssignSet procedure constructs and returns, though the
variable-parameter Target, a freshly allocated copy of the given
set Source. }
procedure AssignSet (var Target: Congeries; Source: Congeries);
{ The DeallocateSet procedure recycles all of the storage associated
with a given set. }
procedure DeallocateSet (var Delend: Congeries);
implement
import
StdErr;
{ The first few definitions set up the exception handler for
the Sets module. }
const
FirstSetExceptionCode = 1;
SelectFromSetException = 1;
SetExceptionException = 2;
LastSetExceptionCode = 2;
{ In this implementation, sets are represented as binary search trees. }
type
BinarySearchTree = Congeries;
SetRecord = record
Datum: Element;
Left, Right: BinarySearchTree
end;
{ Here is the exception handler for sets. }
procedure SetExceptionHandler (ExceptionCode: Integer);
begin
if (ExceptionCode < FirstSetExceptionCode) or
(LastSetExceptionCode < ExceptionCode) then
ExceptionCode := SetExceptionException;
WriteLn (StdErr, 'Exception #', ExceptionCode : 1,
' in module Sets:');
case ExceptionCode of
SelectFromSetException:
WriteLn (StdErr, 'An empty set was passed to the SelectFromSet ',
'function.');
SetExceptionException:
WriteLn (StdErr, 'An unknown exception code was passed to the ',
'SetExceptionHandler procedure.')
end
end;
{ Here are the definitions relating to binary search trees. Deletion is
not needed in this case, since the set operations construct all their
results from scratch. However, a function has been added to return
(without removing) the element at the root of a non-empty binary
search tree, and another function to determine whether every element
in a binary search tree meets a specified condition. }
function MakeEmptyBinarySearchTree: BinarySearchTree;
begin
MakeEmptyBinarySearchTree := nil
end;
function MakeSingletonBinarySearchTree (Elm: Element): BinarySearchTree;
var
Result: BinarySearchTree;
begin
New (Result);
Result^.Datum := Elm;
Result^.Left := MakeEmptyBinarySearchTree;
Result^.Right := MakeEmptyBinarySearchTree;
MakeSingletonBinarySearchTree := Result
end;
function EmptyBinarySearchTree (B: BinarySearchTree): Boolean;
begin
EmptyBinarySearchTree := (B = nil)
end;
{ Note that the insertion procedure discards duplicate elements
instead of storing them in the right subtree. }
procedure InsertIntoBinarySearchTree (Elm: Element;
var B: BinarySearchTree);
begin
if EmptyBinarySearchTree (B) then
B := MakeSingletonBinarySearchTree (Elm)
else if PrecedesElement (Elm, B^.Datum) then
InsertIntoBinarySearchTree (Elm, B^.Left)
else if PrecedesElement (B^.Datum, Elm) then
InsertIntoBinarySearchTree (Elm, B^.Right)
end;
function SearchBinarySearchTree (Sought: Element; B: BinarySearchTree):
Boolean;
begin
if EmptyBinarySearchTree (B) then
SearchBinarySearchTree := False
else if PrecedesElement (Sought, B^.Datum) then
SearchBinarySearchTree := SearchBinarySearchTree (Sought, B^.Left)
else if PrecedesElement (B^.Datum, Sought) then
SearchBinarySearchTree := SearchBinarySearchTree (Sought, B^.Right)
else
SearchBinarySearchTree := True
end;
{ It is a precondition of the ElementAtRootOfBinarySearchTree procedure
that its argument is not nil. Caller beware! }
function ElementAtRootOfBinarySearchTree (B: BinarySearchTree):
Element;
begin
ElementAtRootOfBinarySearchTree := B^.Datum
end;
{ The ApplyThroughoutBinarySearchTree procedure performs a preorder
traversal rather than an inorder traversal in this case, so that
the members of a set are not encountered in ascending order (which
would have a devastating effect on such operations as AssignSet
and IntersectionOfSets -- they would return trees with linear
structure even when given balanced trees as inputs). }
procedure ApplyThroughoutBinarySearchTree (B: BinarySearchTree;
procedure P (Elm: Element));
begin
if not EmptyBinarySearchTree (B) then begin
P (B^.Datum);
ApplyThroughoutBinarySearchTree (B^.Left, P);
ApplyThroughoutBinarySearchTree (B^.Right, P)
end
end;
{ The EveryElementOfBinarySearchTree function determines whether every
element of a binary search tree meets a condition specified by the
Boolean function Test. It basically performs a preorder traversal
of the tree, backing out (and returning False) as soon as it
encounters an element that does not meet the test condition or
returning True at the end of the process if no such element is
encountered. }
function EveryElementOfBinarySearchTree (B: BinarySearchTree;
function Test (Elm: Element): Boolean): Boolean;
begin
if B = nil then
EveryElementOfBinarySearchTree := True
else if not Test (B^.Datum) then
EveryElementOfBinarySearchTree := False
else if EveryElementOfBinarySearchTree (B^.Left, Test) then
EveryElementOfBinarySearchTree :=
EveryElementOfBinarySearchTree (B^.Right, Test)
else
EveryElementOfBinarySearchTree := False
end;
procedure DeallocateBinarySearchTree (var B: BinarySearchTree);
begin
if not EmptyBinarySearchTree (B) then begin
DeallocateBinarySearchTree (B^.Left);
DeallocateBinarySearchTree (B^.Right);
Dispose (B)
end
end;
{ The exported functions and procedures begin here. }
function CreateSingletonSet (Member: Element): Congeries;
var
Result: BinarySearchTree;
begin
Result := MakeEmptyBinarySearchTree;
InsertIntoBinarySearchTree (Member, Result);
CreateSingletonSet := Result
end;
function CreateDoubletonSet (FirstMember, SecondMember: Element):
Congeries;
var
Result: BinarySearchTree;
begin
Result := MakeEmptyBinarySearchTree;
InsertIntoBinarySearchTree (FirstMember, Result);
InsertIntoBinarySearchTree (SecondMember, Result);
CreateDoubletonSet := Result
end;
function MemberOfSet (Candidate: Element; Sett: Congeries): Boolean;
begin
MemberOfSet := SearchBinarySearchTree (Candidate, Sett)
end;
function UnionOfSets (LeftOperand, RightOperand: Congeries): Congeries;
var
Result: BinarySearchTree;
procedure AdjoinToResult (Elm: Element);
begin
InsertIntoBinarySearchTree (Elm, Result)
end;
begin { function UnionOfSets }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (LeftOperand, AdjoinToResult);
ApplyThroughoutBinarySearchTree (RightOperand, AdjoinToResult);
UnionOfSets := Result
end;
function IntersectionOfSets (LeftOperand, RightOperand: Congeries):
Congeries;
var
Result: BinarySearchTree;
procedure ConditionallyAdjoinToResult (Elm: Element);
begin
if SearchBinarySearchTree (Elm, RightOperand) then
InsertIntoBinarySearchTree (Elm, Result)
end;
begin { function IntersectionOfSets }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (LeftOperand,
ConditionallyAdjoinToResult);
IntersectionOfSets := Result
end;
function RelativeComplementOfSets (LeftOperand, RightOperand: Congeries):
Congeries;
var
Result: BinarySearchTree;
procedure ConditionallyAdjoinToResult (Elm: Element);
begin
if not SearchBinarySearchTree (Elm, RightOperand) then
InsertIntoBinarySearchTree (Elm, Result)
end;
begin { function RelativeComplementOfSets }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (LeftOperand,
ConditionallyAdjoinToResult);
RelativeComplementOfSets := Result
end;
function SunderSet (Sett: Congeries;
function Test (Operand: Element): Boolean): Congeries;
var
Result: BinarySearchTree;
procedure ConditionallyAdjoinToResult (Elm: Element);
begin
if Test (Elm) then
InsertIntoBinarySearchTree (Elm, Result)
end;
begin { function SunderSet }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (Sett, ConditionallyAdjoinToResult);
SunderSet := Result
end;
function AdjoinToSet (Sett: Congeries; Adjunct: Element): Congeries;
var
Result: BinarySearchTree;
procedure AdjoinToResult (Elm: Element);
begin
InsertIntoBinarySearchTree (Elm, Result)
end;
begin { function AdjoinToSet }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (Sett, AdjoinToResult);
InsertIntoBinarySearchTree (Adjunct, Result);
AdjoinToSet := Result
end;
function DisjoinFromSet (Sett: Congeries; Disjunct: Element): Congeries;
var
Result: BinarySearchTree;
procedure ConditionallyAdjoinToResult (Elm: Element);
begin
if not EqualElement (Elm, Disjunct) then
InsertIntoBinarySearchTree (Elm, Result)
end;
begin { function DisjoinFromSet }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (Sett, ConditionallyAdjoinToResult);
DisjoinFromSet := Result
end;
function CardinalityOfSet (Operand: Congeries): Integer;
var
Result: Integer;
procedure TallyElement (Elm: Element);
begin
Result := Result + 1
end;
begin { function Cardinality }
Result := 0;
ApplyThroughoutBinarySearchTree (Operand, TallyElement);
CardinalityOfSet := Result
end;
function EmptySet (Operand: Congeries): Boolean;
begin
EmptySet := EmptyBinarySearchTree (Operand)
end;
function EqualSets (LeftOperand, RightOperand: Congeries): Boolean;
begin
if Subset (LeftOperand, RightOperand) then
EqualSets := Subset (RightOperand, LeftOperand)
else
EqualSets := False
end;
function Subset (LeftOperand, RightOperand: Congeries): Boolean;
function MemberOfRightOperand (Elm: Element): Boolean;
begin
MemberOfRightOperand := SearchBinarySearchTree (Elm, RightOperand)
end;
begin { function Subset }
Subset := EveryMemberOfSet (LeftOperand, MemberOfRightOperand)
end;
procedure SelectFromSet (Operand: Congeries; var Selected: Element;
var Remainder: Congeries);
begin
Assert (not EmptySet (Operand), SelectFromSetException,
SetExceptionHandler);
Selected := ElementAtRootOfBinarySearchTree (Operand);
Remainder := DisjoinFromSet (Operand, Selected)
end;
function MapSet (Sett: Congeries;
function Mapper (Operand: Element): Element): Congeries;
var
Result: Congeries;
procedure AdjoinMapOutputToResult (Elm: Element);
begin
InsertIntoBinarySearchTree (Mapper (Elm), Result)
end;
begin { function MapSet }
Result := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (Sett, AdjoinMapOutputToResult);
MapSet := Result
end;
procedure ApplyToEachMemberOfSet (Sett: Congeries;
procedure Applicand (Operand: Element));
begin
ApplyThroughoutBinarySearchTree (Sett, Applicand)
end;
function EveryMemberOfSet (Sett: Congeries;
function Test (Operand: Element): Boolean): Boolean;
begin
EveryMemberOfSet := EveryElementOfBinarySearchTree (Sett, Test)
end;
function SomeMemberOfSet (Sett: Congeries;
function Test (Operand: Element): Boolean): Boolean;
function Complement (Operand: Element): Boolean;
begin
Complement := not Test (Operand)
end;
begin { function SomeMemberOfSet }
SomeMemberOfSet :=
not EveryElementOfBinarySearchTree (Sett, Complement)
end;
procedure AssignSet (var Target: Congeries; Source: Congeries);
procedure AdjoinToTarget (Elm: Element);
begin
InsertIntoBinarySearchTree (Elm, Target)
end;
begin { procedure AssignSet }
Target := MakeEmptyBinarySearchTree;
ApplyThroughoutBinarySearchTree (Source, AdjoinToTarget)
end;
procedure DeallocateSet (var Delend: Congeries);
begin
DeallocateBinarySearchTree (Delend)
end;
end.
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/courses/fundamentals/sets.html