A question was posted asking how you would create in a pointer-free language like ParaSail a doubly-linked list, with constant time addition on either end of the list, and constant time removal anywhere within the list. One straightforward way to do this is to use a Vector to store the nodes, and then use indices into the vector to create the doubly linked list. Below is such an implementation.
// Illustration of a doubly-linked list implemented in ParaSail
// Copyright (C) 2012, AdaCore, New York, NY
// To be used only for Personal, Academic, or Evaluation Purposes;
// Not for Commercial Production Use.
// Report errors at http://groups.google.com/group/parasail-programming-language
// There are two modules here, plus two test programs.
// The first module, Doubly_Linked_Index, provides a doubly-linked
// list of indices, the indices being usable with another vector
// where the actual data can be stored.
// The second module, Doubly_Linked_List, uses the Doubly_Linked_Index
// to provide the "Elem_Id" type, which is used to identify nodes in the
// linked list. It also has a formal type "Element_Type" which is the
// type of the actual data to be stored in the linked list.
interface Doubly_Linked_Index<Max_Len : Univ_Integer := 100_000> is
// Doubly-linked list acting as index into a separate vector of data
type Elem_Id isnew Integer<1..Max_Len>;
op "[]"() -> Doubly_Linked_Index;
// Create an empty linked list
func Append(var DLI : Doubly_Linked_Index) -> Elem_Id;
// Add a new node onto end of linked list
func Prepend(var DLI : Doubly_Linked_Index) -> Elem_Id;
// Add a new node at beginning of linked list
func Insert_Before(var DLI : Doubly_Linked_Index; Elem_Id)
-> optional Elem_Id;
// Insert new node before given element Id
func Insert_After(var DLI : Doubly_Linked_Index; Elem_Id)
-> optional Elem_Id;
// Insert new node after given element Id
func Remove(var DLI : Doubly_Linked_Index; Elem_Id);
// Remove specified element from linked list (if present)
op "in"(Elem_Id; DLI : Doubly_Linked_Index) -> Boolean;
// Return True if given element is in linked list
func Remove_First(var DLI : Doubly_Linked_Index) -> optional Elem_Id;
// Remove first element from linked list
func Remove_Last(var DLI : Doubly_Linked_Index) -> optional Elem_Id;
// Remove last element from linked list
func Remove_Any(var DLI : Doubly_Linked_Index) -> optional Elem_Id;
// Remove arbitrary element from linked list
func Count(DLI : Doubly_Linked_Index) -> Univ_Integer;
// Return count of elements in linked list
func Max_Id_In_Use(DLI : Doubly_Linked_Index) -> optional Elem_Id;
// Return max Elem_Id in use
endinterface Doubly_Linked_Index;
interface Doubly_Linked_List
<Element_Type is Assignable<>;
Max_Len : Univ_Integer := 100_000> is
// Doubly-linked list
type DLI isnew Doubly_Linked_Index<Max_Len>;
type Elem_Id is DLI::Elem_Id;
op "[]"() -> Doubly_Linked_List;
// Create an empty linked list
func Append(var DLL : Doubly_Linked_List; Elem : Element_Type) -> Elem_Id;
// Add a new element onto end of linked list
func Prepend(var DLL : Doubly_Linked_List; Elem : Element_Type) -> Elem_Id;
// Add a new element at beginning of linked list
func Insert_Before
(var DLL : Doubly_Linked_List; Elem : Element_Type; Elem_Id)
-> optional Elem_Id;
// Insert new element before given element Id
func Insert_After
(var DLL : Doubly_Linked_List; Elem : Element_Type; Elem_Id)
-> optional Elem_Id;
// Insert new element after given element Id
func Remove(var DLL : Doubly_Linked_List; Elem_Id);
// Remove specified element from linked list (if present)
op "in"(Elem_Id; DLL : Doubly_Linked_List) -> Boolean;
// Return True if given element is in linked list
func Remove_First(var DLL : Doubly_Linked_List) -> optional Element_Type;
// Remove first element from linked list
func Remove_Last(var DLL : Doubly_Linked_List) -> optional Element_Type;
// Remove last element from linked list
func Remove_Any(var DLL : Doubly_Linked_List) -> optional Element_Type;
// Remove arbitrary element from linked list
func Count(DLL : Doubly_Linked_List) -> Univ_Integer;
// Return count of elements in linked list
func Max_Id_In_Use(DLL : Doubly_Linked_List) -> optional Elem_Id;
// Return max Elem_Id in use
op "indexing"(ref DLL : Doubly_Linked_List; Elem_Id)
-> refoptional Element_Type;
// Return a reference to specified element
endinterface Doubly_Linked_List;
// Here are the implementations of the above two modules
class Doubly_Linked_Index is
interface Node<> is
// Each node on list has next and prev Id
var Next : Elem_Id;
var Prev : Elem_Id;
endinterface Node;
var Links : ZVector<Node>; // zero-th element is header
var First_Free : Elem_Id;
var Num_Free : Univ_Integer;
func Add_Node(var DLI : Doubly_Linked_Index; Next, Prev : Elem_Id)
-> New_Id : Elem_Id is
// Add a node with given Next/Prev fields to Links vector
var New_Node for DLI := Node::(Next => Next, Prev => Prev);
if DLI.First_Free != 0 then
// Reuse a free node`
New_Id := DLI.First_Free;
DLI.First_Free := DLI.Links[New_Id].Next;
DLI.Num_Free -= 1;
DLI.Links[New_Id] <== New_Node;
else
// Add a new node at end of Links vector
New_Id := Length(DLI.Links);
DLI.Links <|= New_Node;
endif;
DLI.Links[Prev].Next := New_Id;
DLI.Links[Next].Prev := New_Id;
return New_Id;
endfunc Add_Node;
// {Num_Free < Length(Links); First_Free in 0..<Length(Links)}
// {(for all N of Links => N.Next in 0..<Length(Links) and then
// N.Prev in 0..<Length(Links)}
exports
op "[]"() -> Doubly_Linked_Index is
return (Links => [(Next => 0, Prev => 0)],
First_Free => 0, Num_Free => 0);
endop "[]";
func Append(var DLI : Doubly_Linked_Index) -> New_Id : Elem_Id is
// Add a new node onto end of linked list
return Add_Node(DLI,
Next => 0, Prev => DLI.Links[0].Prev);
endfunc Append;
func Prepend(var DLI : Doubly_Linked_Index) -> Elem_Id is
// Add a new node at beginning of linked list
return Add_Node(DLI,
Next => DLI.Links[0].Next, Prev => 0);
endfunc Prepend;
func Insert_Before(var DLI : Doubly_Linked_Index; Follower : Elem_Id)
-> New_Id : optional Elem_Id is
// Insert new node before given element Id
if Follower in DLI then
return Add_Node(DLI,
Next => Follower, Prev => DLI.Links[Follower].Prev);
else
// Follower not in linked list
returnnull;
endif;
endfunc Insert_Before;
func Insert_After(var DLI : Doubly_Linked_Index; Elem_Id)
-> optional Elem_Id is
// Insert new node after given element Id
if Elem_Id in DLI then
return Add_Node(DLI,
Next => DLI.Links[Elem_Id].Next, Prev => Elem_Id);
else
// Elem_Id not in linked list
returnnull;
endif;
endfunc Insert_After;
op "in"(Elem_Id; DLI : Doubly_Linked_Index) -> Boolean is
// Return #true if given element is in linked list
// NOTE: All elements on free list have Prev pointing to themselves
return Elem_Id in 1..<Length(DLI.Links) andthen
DLI.Links[Elem_Id].Prev != Elem_Id;
endop "in";
func Remove(var DLI : Doubly_Linked_Index; Elem_Id) is
// Remove specified element from linked list (if present)
if Elem_Id in DLI then
// Not on the free list, so OK to remove
ref Elem => DLI.Links[Elem_Id];
DLI.Links[Elem.Prev].Next := Elem.Next;
DLI.Links[Elem.Next].Prev := Elem.Prev;
// Mark as being on the free list
Elem.Prev := Elem_Id;
// Add to the free list
Elem.Next := DLI.First_Free;
DLI.First_Free := Elem_Id;
DLI.Num_Free += 1;
endif;
endfunc Remove;
func Remove_First(var DLI : Doubly_Linked_Index) -> optional Elem_Id is
// Remove first element from linked list
const First := DLI.Links[0].Next;
if First == 0 then
// List is empty
returnnull;
else
Remove(DLI, First);
return First;
endif;
endfunc Remove_First;
func Remove_Last(var DLI : Doubly_Linked_Index) -> optional Elem_Id is
// Remove last element from linked list
const Last := DLI.Links[0].Prev;
if Last == 0 then
// List is empty
returnnull;
else
Remove(DLI, Last);
return Last;
endif;
endfunc Remove_Last;
func Remove_Any(var DLI : Doubly_Linked_Index) -> optional Elem_Id is
// Remove arbitrary element from linked list
if Count(DLI) mod 2 == 0 then
// Remove First if Count is odd, Remove Last if Count is even
return Remove_Last(DLI);
else
return Remove_First(DLI);
endif;
endfunc Remove_Any;
func Count(DLI : Doubly_Linked_Index) -> Univ_Integer is
// Return count of elements in linked list
return Length(DLI.Links) - DLI.Num_Free - 1;
endfunc Count;
func Max_Id_In_Use(DLI : Doubly_Linked_Index) -> optional Elem_Id is
// Return max Elem_Id in use
return Length(DLI.Links) - 1;
endfunc Max_Id_In_Use;
endclass Doubly_Linked_Index;
class Doubly_Linked_List is
var Index : DLI;
var Data : ZVector<optional Element_Type>;
// zeroth element is used to represent a missing element
exports
op "[]"() -> Doubly_Linked_List is
// Create an empty linked list
return (Index => [], Data => [null]);
endop "[]";
func Append(var DLL : Doubly_Linked_List; Elem : Element_Type)
-> Result : Elem_Id is
// Add a new element onto end of linked list
Result := Append(DLL.Index);
DLL.Data[Result] := Elem;
endfunc Append;
func Prepend(var DLL : Doubly_Linked_List; Elem : Element_Type)
-> Result : Elem_Id is
// Add a new element at beginning of linked list
Result := Prepend(DLL.Index);
DLL.Data[Result] := Elem;
endfunc Prepend;
func Insert_Before
(var DLL : Doubly_Linked_List; Elem : Element_Type; Elem_Id)
-> Result : optional Elem_Id is
// Insert new element before given element Id
Result := Insert_Before(DLL.Index, Elem_Id);
if Result notnullthen
DLL.Data[Result] := Elem;
endif;
endfunc Insert_Before;
func Insert_After
(var DLL : Doubly_Linked_List; Elem : Element_Type; Elem_Id)
-> Result : optional Elem_Id is
// Insert new element after given element Id
Result := Insert_After(DLL.Index, Elem_Id);
if Result notnullthen
DLL.Data[Result] := Elem;
endif;
endfunc Insert_After;
func Remove(var DLL : Doubly_Linked_List; Elem_Id) is
// Remove specified element from linked list (if present)
Remove(DLL.Index, Elem_Id);
endfunc Remove;
op "in"(Elem_Id; DLL : Doubly_Linked_List) -> Boolean is
// Return True if given element is in linked list
return Elem_Id in DLL.Index;
endop "in";
func Remove_First(var DLL : Doubly_Linked_List)
-> Result : optional Element_Type is
// Remove first element from linked list
const Id := Remove_First(DLL.Index);
if Id notnullthen
// Remove element from Data vector
Result <== DLL.Data[Id];
else
// List is empty
Result := null;
endif;
endfunc Remove_First;
func Remove_Last(var DLL : Doubly_Linked_List)
-> Result : optional Element_Type is
// Remove last element from linked list
const Id := Remove_Last(DLL.Index);
if Id notnullthen
// Remove element from Data vector
Result <== DLL.Data[Id];
else
// List is empty
Result := null;
endif;
endfunc Remove_Last;
func Remove_Any(var DLL : Doubly_Linked_List)
-> Result : optional Element_Type is
// Remove arbitrary element from linked list
const Id := Remove_Any(DLL.Index);
if Id notnullthen
// Remove element from Data vector
Result <== DLL.Data[Id];
else
// List is empty
Result := null;
endif;
endfunc Remove_Any;
func Count(DLL : Doubly_Linked_List) -> Univ_Integer is
// Return count of elements in linked list
return Count(DLL.Index);
endfunc Count;
func Max_Id_In_Use(DLL : Doubly_Linked_List) -> optional Elem_Id is
// Return max Elem_Id in use
return Max_Id_In_Use(DLL.Index);
endfunc Max_Id_In_Use;
op "indexing"(ref DLL : Doubly_Linked_List; Elem_Id)
-> refoptional Element_Type is
// Return a reference to specified element
if Elem_Id in DLL.Index andthen
Elem_Id in 1..<Length(DLL.Data) then
return DLL.Data[Elem_Id];
else
// return reference to null value stored in zeroth element
return DLL.Data[0];
endif;
endop "indexing";
endclass Doubly_Linked_List;
// Two test programs, one for the Doubly_Linked_Index module
// and one for the Doubly_Linked_List module.
func Test_DLI() is
var DLI : Doubly_Linked_Index := [];
Println("Append three times");
const Id1 := Append(DLI);
const Id2 := Append(DLI);
const Id3 := Append(DLI);
for Id in DLI forwardloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Prepend twice");
const Id4 := Prepend(DLI);
const Id5 := Prepend(DLI);
Println("Count = " | Count(DLI));
for Id in DLI forwardloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Insert before original second append");
const Id6 := Insert_Before(DLI, Id2);
Println("Count = " | Count(DLI));
for Id in DLI forwardloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Remove " | Id1 | " and " | Id4);
Remove(DLI, Id1);
Remove(DLI, Id4);
Println("Count = " | Count(DLI));
for Id in DLI forwardloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Reverse loop");
for Id in DLI reverseloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Append one and prepend two");
const Id7 := Append(DLI);
const Id8 := Prepend(DLI);
const Id9 := Prepend(DLI);
Println("Count = " | Count(DLI));
for Id in DLI forwardloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Reverse loop");
for Id in DLI reverseloop
Println("Found Elem_Id = " | Id);
endloop;
Println("Unordered loop");
for Id in DLI loop
Println("Found Elem_Id = " | Id);
endloop;
endfunc Test_DLI;
func Test_DLL() is
var DLL : Doubly_Linked_List<Univ_Enumeration> := [];
Println("Append three times");
const Id1 := Append(DLL, #one);
const Id2 := Append(DLL, #two);
const Id3 := Append(DLL, #three);
Println("Count = " | Count(DLL));
Println("Item 2 = " | DLL[Id2]);
Println("Item 3 = " | DLL[Id3]);
var I := 0;
for Elem in DLL forwardloop
Println("Found Elem = " | Elem);
I += 1;
if I > 5 then
exitloop;
endif;
endloop;
Println("Prepend twice");
const Id4 := Prepend(DLL, #four);
const Id5 := Prepend(DLL, #five);
Println("Count = " | Count(DLL));
for Elem in DLL forwardloop
Println("Found Elem = " | Elem);
endloop;
Println("Insert before original second append");
const Id6 := Insert_Before(DLL, #six, Id2);
Println("Count = " | Count(DLL));
for Elem in DLL forwardloop
Println("Found Elem = " | Elem);
endloop;
Println("Remove " | Id1 | " and " | Id4);
Remove(DLL, Id1);
Remove(DLL, Id4);
Println("Count = " | Count(DLL));
for Elem in DLL forwardloop
Println("Found Elem = " | Elem);
endloop;
Println("Reverse loop");
for Elem in DLL reverseloop
Println("Found Elem = " | Elem);
endloop;
Println("Append one and prepend two");
const Id7 := Append(DLL, #seven);
const Id8 := Prepend(DLL, #eight);
const Id9 := Prepend(DLL, #nine);
Println("Count = " | Count(DLL));
for Elem in DLL forwardloop
Println("Found Elem = " | Elem);
endloop;
Println("Reverse loop");
for Elem in DLL reverseloop
Println("Found Elem = " | Elem);
endloop;
Println("Unordered loop");
for Elem in DLL loop
Println("Found Elem = " | Elem);
endloop;
endfunc Test_DLL;