[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Container Library?
(I am placing Michael's reply before my original post, for convenience.)
MVA> Could you give some Oberon-2 pseudo code for this? The involved
MVA> types and their interaction aren't clear to me from this
MVA> description.
Sorry about being too brief. Here is a module which should illustrate
what I mean. (BTW, the module compiles and runs under oo2c v1.3.2.)
(*==================================================================*)
MODULE LinkedList;
IMPORT
Out;
TYPE
(* "Abstract" base type for elements of a linked list *)
Element* = POINTER TO ElementDesc;
ElementDesc* = RECORD
next, prev : Element;
END;
(* The linked list type *)
LinkedList* = POINTER TO ListDesc;
ListDesc* = RECORD
head : Element;
END;
(* A procedure for operating on elements of the linked list *)
Operator* = PROCEDURE (element : Element);
(*------------------------------------------------------------------
Create a linked list.
*)
PROCEDURE CreateList* () : LinkedList;
VAR
list : LinkedList;
sentinel : Element;
BEGIN
NEW(list);
NEW(sentinel);
list.head := sentinel;
sentinel.next := sentinel;
sentinel.prev := sentinel;
RETURN list;
END CreateList;
(*------------------------------------------------------------------
Add an element to the head of a linked list.
*)
PROCEDURE (list : LinkedList) Prepend* (element : Element);
BEGIN
element.next := list.head.next;
element.next.prev := element;
element.prev := list.head;
list.head.next := element;
END Prepend;
(*------------------------------------------------------------------
Delete the element from the linked list.
Do nothing if the element isn't in the list.
*)
PROCEDURE (list : LinkedList) Delete* (element : Element);
VAR
current : Element;
BEGIN
ASSERT(element # list.head);
current := list.head.next;
WHILE (current # element) & (current # list.head) DO
current := current.next;
END;
IF (current = element) & (element # list.head) THEN
element.next.prev := element.prev;
element.prev.next := element.next;
(* Be GC friendly! *)
element.prev := NIL;
element.next := NIL;
END;
END Delete;
(*------------------------------------------------------------------
Get the last element of the list.
Return NIL if the list is empty.
*)
PROCEDURE (list : LinkedList) Last* () : Element;
VAR
element : Element;
BEGIN
element := list.head.prev;
IF element = list.head THEN
element := NIL;
END;
RETURN element;
END Last;
(*------------------------------------------------------------------
Operate on every element of the linked list.
BE CAREFUL: if you modify the list, the operator
must not change the current element's next field.)
*)
PROCEDURE (list : LinkedList) Operate* (operate : Operator);
VAR
current : Element;
BEGIN
current := list.head.next;
WHILE current # list.head DO
operate(current);
current := current.next;
END
END Operate;
(*------------------------------------------------------------------
Test the module.
*)
PROCEDURE Test* ();
TYPE
IntElement = POINTER TO IntElemDesc;
IntElemDesc = RECORD (ElementDesc)
value : INTEGER;
END;
VAR
list : LinkedList;
PROCEDURE Create () : LinkedList;
VAR
i : INTEGER;
list : LinkedList;
element : IntElement;
BEGIN
list := CreateList();
FOR i := 1 TO 10 DO
NEW(element);
element.value := i;
list.Prepend(element);
END;
RETURN list;
END Create;
PROCEDURE Print (element : Element);
VAR
myElement : IntElement;
BEGIN
IF element # list.head THEN
myElement := element(IntElement);
Out.LongInt(myElement.value, 0);
Out.Ln();
END
END Print;
BEGIN (* Test *)
list := Create();
list.Operate(Print);
Out.Ln();
list.Delete(list.Last());
list.Operate(Print);
END Test;
BEGIN (* Linked List *)
Test();
END LinkedList.
(*==================================================================*)
MKG> My design makes each container a record type which manipulates
MKG> pointers to adapter records containing the actual data. Type
MKG> bound procedures are used to perform all the requisite operations
MKG> on the container. (I favor TBP, although conventional procedures
MKG> can be used.) To use the container, one would extend the
MKG> appropriate adapter type to contain the actual data.
To add an INTEGER to the linked list, one needs to define an
"adapter", in this case IntElement, as an extension of the Element
type so data can be placed into the container. In general, an adapter
will need to be made for each type of data which a container holds.
This design provides type safety (but seems a little awkward and
inefficient to me). Either adapters for the built-in types can be
included in each container module of the library or users can be left
to create all adapters. The former choice would increase the size of
executables unless unused types and code can be stripped out. As I
think that would be an unrealistic expectation of the compiler (it is
actually the responsiblity of the linker), I would suggest we let
users create their own adapters.
Mark
--
Mark K. Gardner (mkgardne@cs.uiuc.edu)
University of Illinois at Urbana-Champaign
Real-Time Systems Laboratory
--