Abstract Data Types (ADT)
In any programming language, there are several data types that
are available for programmers to use, such as integers, floating point
numbers, strings or characters. Many applications need more
complicated structures of data that can be added on a program-to-program
basis; hence, the notion of data abstraction.
A data type consists of a set of values or elements, called its
domain, and a set of operators acting on that domain. The
domain may be defined either by enumeration, if finite, or by
specifying a rule describing its elements.
Once the domain is defined, we need to specify the operations. Two steps
are involved here. First, we enumerate operators names,
identifying what type of values they
both operate on and also return. This step is referred to as prescribing
the syntax. Second, we need to describe the operators' functionality, or
semantics. There are several ways to give semantics:
- giving textual description of the behavior
- operational specification
- algebriac specification
Textual description is an informal way of presenting an ADT. Due
to natural language ambiguities, it is neither precise nor preferred
in discussing ADT.
On the other hand, operational specification
gives a description of the ADT at hand in terms of another, primitive
type with well-defined mathematical properties. For example, we may
describe the ADT set in terms of sequences, which is in this case
a well-defined type. In what follows, we concentrate on algebriac
specification.
Algebraic Specification
In giving the algebraic specification of some ADT, the process
is divided into two parts: syntax and semantics. The syntax description
is often referred to as the signature of the algebra, and each
"auxiliary" data type used to give this signature is called a sort.
Concepts will be illustrated via an ADT we will call STACK.
We have, for the moment, a general intuitive idea about what we mean by
a STACK and what kinds of operations are needed in applications for
this data type. These operations are: push, pop,
top, empty, and newstack.
The syntax gives a description of each one
of these operations in the same way we normally describe functions:
take some elements from a domain and map them to some element of
a domain.
For example, syntax for STACK may be given as follows:
newstack: -------> STACK
push : STACK x INTEGER --------> STACK
pop : STACK --------> STACK
top : STACK --------> INTEGER U
{undefined}
empty : STACK --------> BOOLEAN
NOTES:
- INTEGER and BOOLEAN are the types of this algebra.
- The intended meaning for these operators are as follows:
- newstack: takes nothing as its argument and returns an empty
STACK.
- push: takes some STACK S and an INTEGER I, and returns a STACK S1
with top element I and the rest of its elements are from S.
- pop: takes a STACK S and removes its top element.
- top: takes a STACK S, returns the top element of S. If S is
empty, it returns undefined.
- empty: takes a STACK S, decides if it is empty.
- At this stage, implementation is not considered.
Until now, we had only an intuitive idea about the domain of the type
STACK. Using the above operators, we may give a precise
description of STACK.
Among these operators, we identify the least number
necessary to construct all possible STACKs.
These
operators are called constructors for the type.
For
our type STACK, we can see that the operators newstack and
push are the constructors for STACK. In other words, any element of a
STACK type can be built using only operations consisting of newstack
and push. Now we have a precise way to describe the domain of the
type STACK.
In semantics, we describe the meaning of the operators in terms of
how they interact with one another. That will give rise to a set of equations
referred to as the axia. For example, axia for STACK may be given as
follows:
- pop(newstack) = newstack
- pop(push(S,I)) = S
- top(newstack) = undefined
- top(push(S,I)) = I
- empty(newstack) = true
- empty(push(S,I))= false
NOTES:
- S is an arbitrary element of type STACK, and I is an INTEGER.
- At this stage, questions concerning implementation are not an issue.
All we need at this stage is a desciption of operators, and
how they interact with each other.
- The domain of STACK can be described in terms of constructors (newstack
and push), by means of what may be called the normal lemma of the
type STACK, as follows:
For every STACK S, either:
a) S = newstack or,
b) there exists some S1 and I such that S = push(S1,I).
Developing Algebraic Axia
In this section, we give a systematic way to develop
our set of "sufficiantly complete" axia. We will use the example of
the data type SET.
To create our axia, we must:
- Determine Functionality
- Write Signatures
- Choose Constructors
- Write Axia
What we have at the moment is a general notion about the type
SET, a collection of some elements -- integers, for instance. We also have
an idea about the possible use of this type. Hence, we can decide
what kind of operations are needed.
Our first step is to determine functionality. Thinking about the
data type
and its possible use, we determine what operations are needed to create
instances of the data type,
modify these instances, and obtain values from the
data type.
For example, we need to:
- create a new SET. This is done by having an operation called
newset.
- add elements to a set. Thus, we need an operation called add.
- delete an element from a set. Thus, we need an operation called
delete.
- check to see if some given element is a member of a set.
Another operation called member must be used.
- verify if a set is empty. This requires the operation empty.
- count how many elements a set contains. The function size will
be
used.
Do we need any more operations? That depends on the way we intend
to use the type we are building. It may be useful to add
other operations for SET, such as union, intersection, etc. We will keep
things simple, and identify our operations as newset, add, delete,
member,
empty and size.
The next step is to write the signatures. Remember, signatures are
operators expressed as mathematical
functions.
Using INTEGERS and BOOLEAN as types, we have:
newset: --------> SET
add : SET x INTEGER --------> SET
delete: SET x INTEGER --------> SET
member: SET x INTEGER --------> BOOLEAN
empty : SET --------> BOOLEAN
size : SET --------> INTEGER
Next, we must choose constructors. Among the operators mentioned
above, newset and add are the constructors. Any SET can be constructed
using only these two operators.
Finally, we write the axia. We only need to consider the
composition of
each non-constructor operator from some combination of constructor
operators. Thus,
we only consider:
- delete(newset,I)
- delete(add(S,I),J)
- member(newset,I)
- member(add(S,I),J)
- empty(newset)
- empty(add(S,I))
- size(newset)
- size(add(S,I))
Having this list at hand, we need to express each statment in a way
reflecting how the operators interact.
This may be done as
follows:
- delete(newset,I) = newset
- delete(add(S,I),J) = S if I=J else add(delete(S,J),I)
- member(newset,I) = false
- member(add(S,I),J) = true if I=J else member(S,J)
- empty(newset) = true
- empty(add(S,I)) = false
- size(newset) = 0
- size(add(S,I)) = size(S)+1 if I is not in S else size(S)
NOTES:
In the implementation process we will be faced with an issue
concerning term equality. That is, what is meant by stating that
two things are equal? For example, to state that two sets are
equal, it means they have the same elements (regardless of the
order in which they appear). This "equality function" must
be defined in order for us to check the correctness of our
implementation for the axia.
Previous Topic
Next Topic
Created 02/16/98 by salama
Revised 4/28/98