770 likes | 786 Views
Abstract Data Types and Modules. Builtin Data Types. Builtin data types are designed to insulate the user of a language from the implementation of the data type, which is machine dependent
E N D
Builtin Data Types • Builtin data types are designed to insulate the user of a language from the implementation of the data type, which is machine dependent • These data types can be manipulated by a set of builtin operations, whose implementation details are also hidden from the user. The semantics of these operations are completely specified in the language definition
User-Defined Data Types • User-defined data types are built up from data structures created using the builtin data types and type constructors. Their data structures are visible to the user • User-defined data types do not come with any operations other than the accessing operations of the data structures themselves. The functions defined to manipulate a data structure are not directly associated with its data type. The implementation details of these functions are also visible to the user
Abstract Data Types - Specification • Abstract data types (ADT) provide a method for defining a data type and at the same time operations on that type. The operations should be directly associated with the type • The definitions should not depend on any implementation details • The definitions of the operations should include a specification of their semantics
Abstract Data Types - Implementation • A method for collecting the implementation details of the type and its operations in one place • A method for restricting access to these implementation details by programs that use the data type
Abstract Data Types • Modifiability is enhanced by interfaces that are implementation independent, since changes can be made to an implementation without affecting its use by the rest of the program • Security is enhanced by protecting the implementation details from arbitrary modification by other part of the program • Reusability is enhanced by standard interfaces, since the code can be reused by different programs
Abstract Data Types • Encapsulation refers to the collection of all definitions related to a data type in one location and restricting the use of the type to the operations defined at that location • Information hiding refers to the separation of implementation from these definitions and the suppression of these details in the use of the data type
Algebraic Specification • Syntactic specification includes the name of the type and the names of the operations, including a specification of their parameters and returned values. Function notation of mathematics is often used • Semantics specification includes the properties that the operations must possess. In mathematics, the properties of functions are often described by equations or axioms
An Example – Complex Numbers type complex imports realoperations: +: complex complex complex -: complex complex complex *: complex complex complex /: complex complex complex -: complex complex makecomplex: real real complex realpart: complex real imaginarypart: complex real
An Example – Complex Numbers variables: x, y, z: complex; r, s: realaxioms: realpart(makecomplex(r, s)) = r imaginarypart(makecomplex(r, s)) = s realpart(x+y) = realpart(x) + realpart(y) imaginarypart(x+y) = imaginarypart(x) + imaginarypart(y) realpart(x-y) = realpart(x) - realpart(y) imaginarypart(x-y) = imaginarypart(x) - imaginarypart(y) …
An Example – Queue parameterized type queue(element) imports booleanoperations: createq: queue enqueue: queue element queue dequeue: queue queue frontq: queue element emptyq: queue boolean constant
An Example – Queue variables: q: queue; x: elementaxioms: emptyq(createq) = true emptyq(enqueue(q, x)) = false frontq(createq) = error frontq(enqueue(q, x)) = if emptyq(q) then x else frontq(q) dequeue(createq) = error dequeue(enqueue(q, x)) = if emptyq(q) then q else enqueue(dequeue(q), x) error axiom
Constructors and Inspectors • An operation that creates a new object of the data type being defined is called a constructor. Operations createq and enqueue are constructors • An operation that retrieves previously constructed values is called an inspector. Operations dequeue, frontq, and emptyq are inspectors
Predicates and Selectors • Inspectors that return Boolean values are called predicates. Inspector emptyq is a predicate • Inspectors that return non-Boolean values are called selectors. Inspectors dequeue and frontq are selectors
Finding Axiom Set • In general, one needs one axiom for each combination of an inspector with a constructor emptyq(createq) emptyq(enqueue(q, x)) frontq(createq) frontq(enqueue(q, x)) dequeue(createq) = error dequeue(enqueue(q, x))
An Example – Stack type stack(element) imports booleanoperations: createstk: stack push: stack element stack pop: stack stack top: stack element emptystk: stack boolean
An Example – Stack variables: s: stack; x: elementaxioms: emptystk(createstk) = true emptystk(push(s, x)) = false top(createstk) = error top(push(s, x)) = x pop(createstk) = error pop(push(s, x)) = s
Abstract Data Type Mechanisms • A mechanism must have a way of separating the specification and implementation of an ADT • A mechanism must also guarantee that any code outside the ADT definition cannot use details of the implementation, but can operate on a value of the defined type only through the provided operations
ML Example - Queue abstype ‘element Queue = Q of ‘element listwithval createq = Q [ ];fun enqueue (Q lis, elem) = Q (lis @ [elem]);fun dequeue (Q lis) = Q (tl lis);fun frontq (Q lis) = hd lis;fun emptyq (Q [ ]) = true | emptyq (Q (h::t)) = false;end;
ML Example - Queue type ‘a Queue val createq = - : ‘a Queuefun enqueue = fn : ‘a Queue * ‘a -> ‘a Queuefun dequeue = fn : ‘a Queue -> ‘a Queue fun frontq = fn : ‘a Queue -> ‘a fun emptyq = fn : ‘a Queue -> bool
ML Example - Queue - val q = enqueue(createq, 3); val q = - : int Queue - val q2 = enqueue(q, 4); val q2 = - : int Queue - frontq q2; val it = 3 : int - val q3 = dequeue q2; val q3 = - : int Queue - frontq q3; val it = 4 : int
ML Example – Complex Numbers abstype Complex = C of real * realwithval makecomplex (x, y) = C (x, y);fun realpart (C (r, i)) = r;fun imaginarypart (C (r, i)) = i;fun +: (C (r1, i1), C (r2, i2)) = C (r1+r2, i1+i2);infix 6 +: ; (* other operations *) end;
ML Example – Complex Numbers - val z = makecomplex(1.0, 2.0); val z = - : Complex - val w = makecomplex(2.0, ~1.0); val w = - : Complex - val x = z +: w; val x = - : Complex - realpart x; val it = 3.0 : real - imaginarypart x; val it = 1.0 : real
Modules • An ADT mechanism may not fit a package of related functions that do not associate directly with a data type • A mathematical function package consisting of the standard functions such as sine, cosine, exponential, and logarithmic functions is an example • A module mechanism is used in such cases
Modules • A module is a program unit with a public interface and a private implementation; all services that are available from a module are described in its public interface and are exported to other modules, and all services that are needed by a module must be imported from other modules • As providers of services, modules can export any mix of data types, procedures, variables, and constants
Modules • Because modules have explicit (public) interfaces and separate (private) implementations, they are ideal mechanisms to provide separate compilation and library facilities within a software development environment • The interface for each module is kept in textual form and is needed at the compile time, while the implementation may only be provided as object code and is only needed at the link time
Modules • Modules are an essential tool in program decomposition and complexity control • Modules provide additional scope features that make the task of name control more manageable • Module mechanism can document the dependencies of a module on other modules by requiring explicit import lists whenever code from other modules is used
Separate Compilation in C queue.h(interface) queue.c(implementation) q_user.h(client)
queue.h #ifndef QUEUE_H #define QUEUE_H struct Queuerep; typedef struct Queuerep * Queue; Queue createq(void); Queue enqueue(Queue q, void* elem); void* frontq(Queue q); Queue dequeue(Queue q); int emptyq(Queue q); #endif
queue.c #include "queue.h" #include <stdlib.h> struct Queuerep { void* data; Queue next; }; Queue createq(void) { return 0; } Queue enqueue(Queue q, void* elem) { Queue temp = malloc(sizeof(struct Queuerep)); temp->data = elem; if (q) { temp->next = q->next; q->next = temp; q = temp; } else { q = temp; q->next = temp; } return q; }
queue.c void* frontq(Queue q) { return q->next->data; } Queue dequeue(Queue q) { Queue temp; if (q == q->next) { temp = q; q = 0; } else { temp = q->next; q->next = q->next->next; } free(temp); return q; } int emptyq(Queue q) { return q == 0; }
q_user.c #include <stdlib.h> #include <stdio.h> #include "queue.h" main() { int *x = malloc(sizeof(int)); int *y = malloc(sizeof(int)); int *z; Queue q = createq(); *x = 2; *y = 3; q = enqueue(q,x); q = enqueue(q,y); z = (int*) frontq(q); printf("%d\n",*z); /* prints 2 */ q = dequeue(q); z = (int*) frontq(q); printf("%d\n",*z); /* prints 3 */ return 0; } User can directly copy declarations in queue.h here User can directly copy the definition of Queuerep here
C++ Namespaces - queue.h #ifndef QUEUE_H #define QUEUE_H namespaceMyQueue { struct Queuerep; typedef Queuerep * Queue; Queue createq(); Queue enqueue(Queue q, void* elem); void* frontq(Queue q); Queue dequeue(Queue q); bool emptyq(Queue q); } #endif
queue.cpp namespace MyQueue { struct Queuerep { void* data; Queue next; }; Queue createq(void) { return 0; } Queue enqueue(Queue q, void* elem) { Queue temp = new Queuerep; temp->data = elem; if (q) { temp->next = q->next; q->next = temp; q = temp; } else { q = temp; q->next = temp; } return q; }
queue.cpp void* frontq(Queue q) { return q->next->data; } Queue dequeue(Queue q) { Queue temp; if (q == q->next) { temp = q; q = 0; } else { temp = q->next; q->next = q->next->next; } delete temp; return q; } bool emptyq(Queue q) { return q == 0; } } // end namespace MyQueue
q_user.cpp #include <iostream> #include "queue2.h" using std::endl; using namespace MyQueue; main() { int *x = new int; int *y = new int; int *z; Queue q = MyQueue::createq(); *x = 2; *y = 3; q = enqueue(q,x); q = enqueue(q,y); z = (int*) frontq(q); // explicit qualification needed for cout std::cout << *z << endl; // prints 2 q = dequeue(q); z = (int*) frontq(q); std::cout << *z << endl; // prints 3 }
Java Packages • Packages are constructed as groups of related classes • Each separately compiled file is allowed to have only one public class, and this class can be placed in a package by writing a package declaration at the beginning of the filepackage myqueue; • Names can be dereferenced usingimport myqueue.Queue;import myqueue.*;
Ada Packages • An Ada package is divided into a package specification and a package body • A package specification is the public interface to the package
An Example – Complex Numbers package ComplexNumbers is type Complex is private; function "+"(x,y: in Complex) return Complex; function "-"(x,y: in Complex) return Complex; function "*"(x,y: in Complex) return Complex; function "/"(x,y: in Complex) return Complex; function "-"(z: in Complex) return Complex; function makeComplex (x,y: in Float) return Complex; function realPart (z: in Complex) return Float; function imaginaryPart (z: in Complex) return Float; private type ComplexRecord; type Complex is access ComplexRecord; end ComplexNumbers;
An Example – Complex Numbers package body ComplexNumbers is type ComplexRecord is record re, im: Float; end record; function "+"(x,y: in Complex) return Complex is t: Complex; begin t := new ComplexRecord; t.re := x.re + y.re; t.im := x.im + y.im; return t; end "+"; -- more operations here
An Example – Complex Numbers function makeComplex (x,y: in Float) return Complex is begin return new ComplexRecord'(re => x , im => y); end makeComplex; function realPart (z: in Complex) return Float is begin return z.re; end realPart; function imaginaryPart (z: in Complex) return Float is begin return z.im; end imaginaryPart; end ComplexNumbers;
An Example – Complex Numbers with ComplexNumbers; use ComplexNumbers; procedure ComplexUser is z,w: ComplexNumbers.Complex; begin z := ComplexNumbers.makeComplex(1.0,-1.0); w := ComplexNumbers."+"(z,z); w := z + z; end ComplexUser;
An Example – Queues generic type T is private; package Queues is type Queue is private; function createq return Queue; function enqueue(q:Queue;elem:T) return Queue; function frontq(q:Queue) return T; function dequeue(q:Queue) return Queue; function emptyq(q:Queue) return Boolean; private type Queuerep; type Queue is access Queuerep; end Queues;
An Example – Queues package body Queues is type Queuerep is record data: T; next: Queue; end record; function createq return Queue is begin return null; end createq; function enqueue(q:Queue;elem:T) return Queue is temp: Queue; begin temp := new Queuerep; temp.data := elem; if (q /= null) then temp.next := q.next; q.next := temp; else temp.next := temp; end if; return temp; end enqueue;
An Example – Queues function frontq(q:Queue) return T is begin return q.next.data; end frontq; function dequeue(q:Queue) return Queue is begin if q = q.next then return null; else q.next := q.next.next; return q.next; end if; end dequeue; function emptyq(q:Queue) return Boolean is begin return q = null; end emptyq; end Queues;
An Example – Queues with Queues; procedure Quser is package IntQueues is new Queues(Integer); use IntQueues; package FloatQueues is new Queues(Float); use FloatQueues; fq: FloatQueues.Queue := createq; iq: IntQueues.Queue := createq; begin fq := enqueue(fq,3.1); fq := enqueue(fq,2.3); iq := enqueue(iq,3); iq := enqueue(iq,2); put(frontq(iq)); new_line; fq := dequeue(fq); put(frontq(fq)); new_line; end Quser;
Modules in ML • ML module facility consists of three mechanisms: signatures, structures, and functors • A signature is an interface definition • A structure is an implementation of a signature • Functors are functions from structures to structures
An Example - Queue signature QUEUE = sig type 'a Queue val createq: 'a Queue val enqueue: 'a Queue * 'a -> 'a Queue val frontq: 'a Queue -> 'a val dequeue: 'a Queue -> 'a Queue val emptyq: 'a Queue -> bool end;
An Example - Queue structure Queue1: QUEUE = struct datatype 'a Queue = Q of 'a list val createq = Q [ ]; fun enqueue(Q lis, elem) = Q (lis @ [elem]); fun frontq (Q lis) = hd lis; fun dequeue (Q lis) = Q (tl lis); fun emptyq (Q [ ]) = true | emptyq (Q (h::t)) = false; end;
An Example - Queue - val q = Queue1.enqueue(Queue1.createq,3); val q = Q [3] : int Queue1.Queue - Queue1.frontq q; val it = 3 : int - val q1 = Queue1.dequeue q; val q1 = Q [ ] : int Queue1.Queue - Queue1.emptyq q1; val it = true : bool