One deficiency of the Lists module is that it provides no
simple and efficient way for the application program to ``keep its place''
within a list -- for example, to traverse part of the list, then pause for
computation, then pick up where it left off. One could store the position
number of the last element seen as an Integer and use
RecoverByPositionFromList to get each element as it is needed,
but this is inefficient because each call to
RecoverByPositionFromList starts over at the beginning of the
list and counts elements until it reaches the correct one. One would like
to be able to simply advance from each element to the next. Alternatively,
one could use the FirstOfList and
DeleteFirstOfList functions to take off one element at a time
for processing; unfortunately, this approach destroys the list as a side
effect and so requires the programmer to make a copy of the list first.
The solution to the problem is to add a ``cursor,'' or position marker, to the internal structure of the list and to extend the list interface with procedures and functions for manipulating the cursor. Typically, the cursor has a distinct value for each position in the list, from the first to the last, and in addition a ``null cursor'' value that can act as a sentinel value in loops; advancing the cursor beyond the end of the list makes it a null cursor.
Cursors are used in application programs in many of the ways pointers are used for traversal in standard Pascal; but since they can only be operated on with the procedures and functions exported from the module, application programmers are less likely to make errors with them.
The procedures and functions that operate primarily on the cursor of a list can be specified as follows:
cursor-to-start
Input: operand, a list.
Outputs: none.
Preconditions: none.
Postconditions: If the length of operand is 0, its cursor is
the null cursor; otherwise, its cursor is marking its first position.
advance-cursor
Input: operand, a list.
Outputs: none.
Precondition: The cursor of operand is not null.
Postconditions: If the cursor of operand at input marks the last
position in operand, then it is a null cursor at output.
Otherwise, the cursor of operand at output marks the position
following the position that it marked at input.
null-cursor
Input: operand, a list.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if the cursor of
operand is null and false if it is not.
element-at-cursor
Input: operand, a list.
Output: result, a value of the element type of
operand.
Precondition: The cursor of operand is not null.
Postcondition: result is the element at position
marked by the cursor of operand.
assign-at-cursor
Inputs: ls, a list, and new-value, a value of the
list's element type..
Outputs: none.
Precondition: The cursor of operand is not null.
Postcondition: The length of ls at output is the length of
ls at input. At output, the element at the position marked by
the cursor of ls is new-value, and the element at
any other position is the same as the element at that position in
ls at input.
insert-at-cursor
Inputs: ls, a list, and elm, a value of the
list's element type.
Outputs: none.
Preconditions: none.
Postconditions: The length of ls at output is one more than
the length of ls at input. At output, elm is the
last element of ls if the cursor of ls was null
at input; otherwise, it is the element at the position p that was
marked by the cursor of ls at input. For every natural number
k from one to the length of ls at input, the kth
element of ls at input is the kth element of
ls at output if the cursor of ls was null at
input or if k is less than p, and the (k + 1)st
element of ls at output if the cursor of ls was
not null at input and k is greater than or equal to p. The
cursor of ls is null at output if it was null at input;
otherwise, it marks position p + 1.
delete-at-cursor
Inputs: ls, a list.
Outputs: none.
Precondition: The cursor of ls is not null.
Postconditions: The length of ls at output is one less than
the length of ls at input. For every natural number k
from one to the length of ls at output, the kth element
of ls at output is the kth element of ls
at input if k is less than the position p marked by the
cursor of ls at input and the (k + 1)st element of
ls at input if k is greater than or equal to p.
The cursor of ls is null at output if p is the last
position of ls at input; otherwise, it marks position
p at output.
find-next
Inputs: sought and operand, where
operand is a list and sought is a value of its
element type.
Outputs: none.
Preconditions: none.
Postconditions: Let p be 0 if the cursor of operand was
null at input; otherwise, let p be the position it marked. Either
the cursor of operand at output is null and
sought is not in any position greater than p, or
sought is the element at the position q marked by the
cursor of operand at output, q is greater than
p, and sought is not in any position greater than
p and less than q.
find-next-by-test
Inputs: operand, a list, and test, an operation
that takes one input, a value of the element type of operand,
and yields one output, a Boolean.
Outputs: none.
Preconditions: none.
Postconditions: Let p be 0 if the cursor of operand was
null at input; otherwise, let p be the position it marked. Either
(1) the cursor of operand at output is null and applying
test to the element in any position greater than p
yields false, or (2) applying test to the element at
the position q marked by the cursor of operand at
output yields true, q is greater than p, and applying
test to the element in any of the positions greater than
p and less than q yields false.
Also, now that lists have cursors, we need to run through the specifications for the previously defined list operations and how cursors are affected in each one. The key here is to make the rules as simple and general as possible, so that the application programmer does not have a hard time keeping track of the cursor's position when performing a series of operations in which it is not used. It turns out that three rules cover all the cases:
ListRecord, set to Nil when the
cursor is null and otherwise pointing to the appropriate component of the
linked list.
Two of the new operations, insert-at-cursor and
delete-at-cursor, have difficult special cases. The difficulty is
the same both times: When inserting or deleting a component, the most
natural way to proceed entails modifying the pointer in the immediately
preceding component of the linked list; but there's no easy way to access
this component given the cursor -- the pointers point in the wrong
direction. If the insertion or deletion is not occurring at the very end
of the list, we can use the clever dodge of copying data from one component
into another while leaving the pointer structure unchanged; but this will
not work at the end of the list, since the pointer in the preceding
component must be changed from Nil to non-Nil (in
an insertion) or from non-Nil to Nil (in a
deletion).
There are a couple of ways of handling this problem, both somewhat
unsatisfactory. It would be possible to add yet another pointer field to
ListRecord, for a pointer to the component immediately
preceding the one the cursor points to; this makes insertion and deletion
efficient even at the end of the list, but it slows down all the other
operations that affect the cursor. Alternatively, we could just put up
with the inefficiency of starting at the beginning of the list and
traversing down to the end whenever we had to perform one of the difficult
cases of insert-at-cursor and delete-at-cursor. This is the
approach adopted below.
Here is the HP Pascal implementation:
{ This module defines an interface for a list data type that includes a
cursor in each list and implements it for HP 9000 Series 700 workstations
under HP-UX 9.x, using HP Pascal.
Programmer: John Stone, Grinnell College.
Original version: August 17, 1996.
}
$heap_dispose on$
module ListsWithCursors;
$search 'elements.o'$
import Elements;
export
type
List = ^ListRecord; { an opaque type }
{ The MakeEmptyList function creates and returns a newly allocated,
empty list. }
function MakeEmptyList: List;
{ The PrependToList procedure takes a list and prepends an element to
it. }
procedure PrependToList (Prefix: Element; var Base: List);
{ The FirstOfList function returns the first element of a non-empty
list. }
function FirstOfList (Operand: List): Element;
{ The DeleteFirstOfList function takes a non-empty list and removes its
first element. }
procedure DeleteFirstOfList (var Operand: List);
{ The EmptyList function determines whether a given list is empty. }
function EmptyList (Operand: List): Boolean;
{ The LengthOfList function returns the length of a given list. }
function LengthOfList (Operand: List): Integer;
{ The AppendToList procedure takes a list and appends an element to it. }
procedure AppendToList (var Base: List; Postfix: Element);
{ The LastOfList function returns the last element of a non-empty list. }
function LastOfList (Operand: List): Element;
{ The DeleteLastOfList procedure takes a non-empty list and removes its
last element. }
procedure DeleteLastOfList (var Operand: List);
{ The CopyList function constructs and returns a copy of a given list,
in newly allocated storage. }
function CopyList (Operand: List): List;
{ The CursorToStartOfList procedure adjusts the list's cursor so that it
marks the first position (or is null, if the list is empty). }
procedure CursorToStartOfList (var Operand: List);
{ The AdvanceCursorAlongList procedure moves the list's cursor, assumed
not to be null, to the next position on the list (or makes it null, if
there are no more positions. }
procedure AdvanceCursorAlongList (var Operand: List);
{ The NullCursorInList function determines whether a given list's cursor
is null. }
function NullCursorInList (Operand: List): Boolean;
{ The ConcatenateList procedure takes two lists and adds the elements
of the second operand at the end of the first operand. }
procedure ConcatenateList (var LeftOperand: List; RightOperand: List);
{ The RecoverByPositionFromList function returns the value occupying
a specified position in a list. }
function RecoverByPositionFromList (Position: Integer; Ls: List):
Element;
{ The ElementAtCursorInList function returns the element occupying the
position marked by the cursor in a given list. }
function ElementAtCursorInList (Operand: List): Element;
{ The AssignAtPositionInList procedure replaces the value occupying a
specified position in a list with a new value. }
procedure AssignAtPositionInList (Position: Integer; var Ls: List;
NewValue: Element);
{ The AssignAtCursorInList procedure replaces the element occupying the
position marked by the cursor in a given list with a new value. }
procedure AssignAtCursorInList (var Ls: List; NewValue: Element);
{ The ElementOfList function determines whether a given value is an
element of a given list. }
function ElementOfList (Candidate: Element; Ls: List): Boolean;
{ The LocateInList procedure determines whether a given value is an
element of a given list and, if so, returns the least position at which
it occurs. }
procedure LocateInList (Sought: Element; Ls: List; var Found: Boolean;
var Position: Integer);
{ The FindNextInList procedure advances a given list's cursor to the next
position occupied by a specified value, leaving it null if there is no
such position. }
procedure FindNextInList (Sought: Element; var Ls: List);
{ The Sublist function takes a list and prunes off all of it but a
section bounded by the positions indicated by Start and Finish. }
procedure Sublist (var Ls: List; Start, Finish: Integer);
{ The ReverseList procedure reverses the order of the elements in a given
list. }
procedure ReverseList (var Operand: List);
{ The InsertAtPositionInList procedure inserts an element at a specified
position in a list. }
procedure InsertAtPositionInList (var Ls: List; Position: Integer;
Elm: Element);
{ The InsertAtCursorInList procedure inserts a new element into a list
at the position marked by the list's cursor, or at the end if the
list's cursor is null. }
procedure InsertAtCursorInList (var Ls: List; Elm: Element);
{ The DeleteAtPositionInList procedure removes the element at a specified
position in a list. }
procedure DeleteAtPositionInList (var Ls: List; Position: Integer);
{ The DeleteAtCursorInList procedure deletes and discards the element
at the position in a given list that is marked by the list's cursor. }
procedure DeleteAtCursorInList (var Ls: List);
{ The DeleteValueFromList procedure strips all occurrences of a specified
value out of a list. }
procedure DeleteValueFromList (var Ls: List; Delend: Element);
{ The ReplaceInList procedure replaces every occurrence of a specified
value in a list with a new value. }
procedure ReplaceInList (var Ls: List; Displacer, Displaced: Element);
{ The FillList function constructs and returns a list consisting
of a specified number of copies of a given element. }
function FillList (Length: Integer; Filler: Element): List;
{ The GenerateList function constructs a list of a specified
length by applying a given function to the positive integers in
ascending order until that length is reached. }
function GenerateList (function Generator (N: Integer): Element;
Length: Integer): List;
{ The TransformList procedure applies a transformation to every element
of a list. }
procedure TransformList (var Ls: List;
function Transformer (E: Element): Element);
{ The ApplyAlongList procedure applies a given procedure to each
successive element of a given list. }
procedure ApplyAlongList (Ls: List; procedure Applicand (E: Element));
{ The EveryElementOfList function determines whether every element
of a given list satisfies a given predicate. }
function EveryElementOfList (Ls: List;
function Test (E: Element): Boolean): Boolean;
{ The SomeElementOfList function determines whether at least one
element of a given list satisfies a given predicate. }
function SomeElementOfList (Ls: List;
function Test (E: Element): Boolean): Boolean;
{ The RecoverByTestFromList procedure determines whether any of the
elements of a given list satisfies a given predicate and, if so,
returns the one whose position is the least. }
procedure RecoverByTestFromList (Ls: List;
function Test (E: Element): Boolean; var Found: Boolean;
var Sought: Element);
{ The LocateByTestInList procedure determines whether any of the
elements of a given list satisfies a given predicate and, if so,
returns the least position occupied by such an element. }
procedure LocateByTestInList (Ls: List;
function Test (E: Element): Boolean; var Found: Boolean;
var Position: Integer);
{ The FindNextByTestInList procedure advances a given list's cursor to
the next position occupied by an element that satisfies a given
predicate, leaving it null if there is no such position. }
procedure FindNextByTestInList (var Operand: List;
function Test (E: Element): Boolean);
{ The FilterList procedure strips out of a given list any elements that
do not satisfy a given predicate. }
procedure FilterList (var Ls: List; function Test (E: Element): Boolean);
{ The DeallocateList procedure recycles the storage associated with
a given list. }
procedure DeallocateList (var Operand: List);
implement
import StdErr;
const
FirstExceptionCode = 1;
UninitializedListException = 1;
FirstOfListException = 2;
DeleteFirstOfListException = 3;
LastOfListException = 4;
DeleteLastOfListException = 5;
NullCursorException = 6;
RecoverByPositionFromListException = 7;
AssignAtPositionInListException = 8;
SublistException = 9;
InsertAtPositionInListException = 10;
DeleteAtPositionInListException = 11;
FillListException = 12;
GenerateListException = 13;
ExceptionException = 14;
LastExceptionCode = 14;
type
Link = ^ListComponent;
ListComponent = record
Datum: Element;
Next: Link
end;
ListRecord = record
Size: Integer;
Head: Link;
Cursor: Link
end;
procedure ListExceptionHandler (ExceptionCode: Integer);
begin
if (ExceptionCode < FirstExceptionCode) or
(LastExceptionCode < ExceptionCode) then
ExceptionCode := ExceptionException;
WriteLn (StdErr, 'Exception #', ExceptionCode : 1,
' in module Lists:');
case ExceptionCode of
UninitializedListException:
WriteLn (Stderr, 'An operation was applied to an uninitialized ',
'list. ');
FirstOfListException:
WriteLn (StdErr, 'The empty list was passed to the ',
'FirstOfList function.');
DeleteFirstOfListException:
WriteLn (StdErr, 'The empty list was passed to the ',
'DeleteFirstOfList function.');
LastOfListException:
WriteLn (StdErr, 'The empty list was passed to the ',
'LastOfList function.');
DeleteLastOfListException:
WriteLn (StdErr, 'The empty list was passed to the ',
'DeleteLastOfList function.');
NullCursorException:
WriteLn (StdErr, 'A list with a null cursor was passed to a ',
'procedure or function that required a non-null cursor.');
RecoverByPositionFromListException:
WriteLn (StdErr, 'An invalid index was passed to the ',
'RecoverByPositionFromList function.');
AssignAtPositionInListException:
WriteLn (StdErr, 'An invalid index was passed to the ',
'AssignAtPositionFromList function.');
SublistException:
WriteLn (StdErr, 'An invalid index was passed to the Sublist ',
'function.');
InsertAtPositionInListException:
WriteLn (StdErr, 'An invalid index was passed to the ',
'InsertAtPositionInList function.');
DeleteAtPositionInListException:
WriteLn (StdErr, 'An invalid index was passed to the ',
'DeleteAtPositionInList function.');
FillListException:
WriteLn (StdErr, 'A negative Length argument was passed to the ',
'FillList function.');
GenerateListException:
WriteLn (StdErr, 'A negative Length argument was passed to the ',
'GenerateList function.');
ExceptionException:
WriteLn (StdErr, 'The ListExceptionHandler procedure received ',
'an unknown exception code.')
end
end;
function CopyHelper (Hook: Link): Link;
var
OneMore: Link;
begin
if Hook = Nil then
CopyHelper := Nil
else begin
New (OneMore);
OneMore^.Datum := Hook^.Datum;
OneMore^.Next := CopyHelper (Hook^.Next);
CopyHelper := OneMore
end
end;
procedure DisposeRest (var Rest: Link);
begin
if Rest <> Nil then begin
DisposeRest (Rest^.Next);
Dispose (Rest)
end
end;
function MakeEmptyList: List;
var
Result: List;
begin
New (Result);
Result^.Size := 0;
Result^.Head := Nil;
Result^.Cursor := Nil;
MakeEmptyList := Result
end;
procedure PrependToList (Prefix: Element; var Base: List);
var
OneMore: Link;
begin
Assert (Base <> Nil, UninitializedListException,
ListExceptionHandler);
Base^.Size := Base^.Size + 1;
New (OneMore);
OneMore^.Datum := Prefix;
OneMore^.Next := Base^.Head;
Base^.Head := OneMore;
Base^.Cursor := Nil
end;
function FirstOfList (Operand: List): Element;
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Assert (Operand^.Head <> Nil, FirstOfListException,
ListExceptionHandler);
FirstOfList := Operand^.Head^.Datum
end;
procedure DeleteFirstOfList (var Operand: List);
var
OneLess: Link;
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Assert (Operand^.Head <> Nil, DeleteFirstOfListException,
ListExceptionHandler);
Operand^.Size := Operand^.Size - 1;
OneLess := Operand^.Head;
Operand^.Head := OneLess^.Next;
Dispose (OneLess);
Operand^.Cursor := Nil
end;
function EmptyList (Operand: List): Boolean;
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
EmptyList := (Operand^.Head = Nil)
end;
function LengthOfList (Operand: List): Integer;
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
LengthOfList := Operand^.Size
end;
procedure AppendToList (var Base: List; Postfix: Element);
var
OneMore: Link;
procedure AttachAtEnd (var Hook: Link);
begin
if Hook = Nil then
Hook := OneMore
else
AttachAtEnd (Hook^.Next)
end;
begin { procedure AppendToList }
Assert (Base <> Nil, UninitializedListException, ListExceptionHandler);
Base^.Size := Base^.Size + 1;
New (OneMore);
OneMore^.Datum := Postfix;
OneMore^.Next := Nil;
AttachAtEnd (Base^.Head);
Base^.Cursor := Nil
end;
function LastOfList (Operand: List): Element;
function LastHelper (Hook: Link): Element;
begin
if Hook^.Next = Nil then
LastHelper := Hook^.Datum
else
LastHelper := LastHelper (Hook^.Next)
end;
begin { function LastOfList }
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Assert (Operand^.Head <> Nil, LastOfListException,
ListExceptionHandler);
LastOfList := LastHelper (Operand^.Head)
end;
procedure DeleteLastOfList (var Operand: List);
procedure DeleteHelper (var Hook: Link);
var
OneLess: Link;
begin
if Hook^.Next = Nil then begin
OneLess := Hook;
Hook := Nil;
Dispose (OneLess)
end
else
DeleteHelper (Hook^.Next)
end;
begin { procedure DeleteLastOfList }
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Assert (Operand^.Head <> Nil, DeleteLastOfListException,
ListExceptionHandler);
Operand^.Size := Operand^.Size - 1;
DeleteHelper (Operand^.Head);
Operand^.Cursor := Nil
end;
function CopyList (Operand: List): List;
var
Result: List;
begin { function CopyList }
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
New (Result);
Result^.Size := Operand^.Size;
Result^.Head := CopyHelper (Operand^.Head);
Result^.Cursor := Nil;
CopyList := Result
end;
procedure CursorToStartOfList (var Operand: List);
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Operand^.Cursor := Operand^.Head
end;
procedure AdvanceCursorAlongList (var Operand: List);
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Assert (Operand^.Cursor <> Nil, NullCursorException,
ListExceptionHandler);
Operand^.Cursor := Operand^.Cursor^.Next
end;
function NullCursorInList (Operand: List): Boolean;
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
NullCursorInList := (Operand^.Cursor = Nil)
end;
procedure ConcatenateList (var LeftOperand: List; RightOperand: List);
procedure ConcatenateHelper (var Hook: Link);
begin
if Hook = Nil then
Hook := CopyHelper (RightOperand^.Head)
else
ConcatenateHelper (Hook^.Next)
end;
begin { ConcatenateList }
Assert ((LeftOperand <> Nil) and (RightOperand <> Nil),
UninitializedListException, ListExceptionHandler);
LeftOperand^.Size := LeftOperand^.Size + RightOperand^.Size;
ConcatenateHelper (LeftOperand^.Head);
LeftOperand^.Cursor := Nil
end;
function RecoverByPositionFromList (Position: Integer; Ls: List):
Element;
function RecoverHelper (Rest: Link; Current: Integer): Element;
begin
if Current = Position then
RecoverHelper := Rest^.Datum
else
RecoverHelper := RecoverHelper (Rest^.Next, Current + 1)
end;
begin { function RecoverByPositionFromList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert ((1 <= Position) and (Position <= Ls^.Size),
RecoverByPositionFromListException,
ListExceptionHandler);
RecoverByPositionFromList := RecoverHelper (Ls^.Head, 1)
end;
function ElementAtCursorInList (Operand: List): Element;
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
Assert (Operand^.Cursor <> Nil, NullCursorException,
ListExceptionHandler);
ElementAtCursorInList := Operand^.Cursor^.Datum
end;
procedure AssignAtPositionInList (Position: Integer; var Ls: List;
NewValue: Element);
procedure AssignHelper (Rest: Link; Current: Integer);
begin
if Current = Position then
Rest^.Datum := NewValue
else
AssignHelper (Rest^.Next, Current + 1)
end;
begin { AssignAtPositionInList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert ((1 <= Position) and (Position <= Ls^.Size),
AssignAtPositionInListException,
ListExceptionHandler);
AssignHelper (Ls^.Head, 1)
end;
procedure AssignAtCursorInList (var Ls: List; NewValue: Element);
begin
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert (Ls^.Cursor <> Nil, NullCursorException, ListExceptionHandler);
Ls^.Cursor^.Datum := NewValue
end;
function ElementOfList (Candidate: Element; Ls: List): Boolean;
function ElementHelper (Rest: Link): Boolean;
begin
if Rest = Nil then
ElementHelper := False
else if EqualElement (Rest^.Datum, Candidate) then
ElementHelper := True
else
ElementHelper := ElementHelper (Rest^.Next)
end;
begin { function ElementOfList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
ElementOfList := ElementHelper (Ls^.Head)
end;
procedure LocateInList (Sought: Element; Ls: List; var Found: Boolean;
var Position: Integer);
procedure LocateHelper (Rest: Link; Current: Integer);
begin
if Rest = Nil then
Found := False
else if EqualElement (Rest^.Datum, Sought) then begin
Found := True;
Position := Current
end
else
LocateHelper (Rest^.Next, Current + 1)
end;
begin { procedure LocateInList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
LocateHelper (Ls^.Head, 1)
end;
procedure FindNextInList (Sought: Element; var Ls: List);
label
99; { Exit when the element is found or the end of the list
is reached. }
begin
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
if Ls^.Cursor = Nil then
Ls^.Cursor := Ls^.Head
else
Ls^.Cursor := Ls^.Cursor^.Next;
while True do
if Ls^.Cursor = Nil then
goto 99
else if EqualElement (Ls^.Cursor^.Datum, Sought) then
goto 99
else
Ls^.Cursor := Ls^.Cursor^.Next;
99:
end;
procedure Sublist (var Ls: List; Start, Finish: Integer);
procedure SublistHelper (Rest: Link; Current: Integer);
begin
if Current < Start then begin
SublistHelper (Rest^.Next, Current + 1);
Dispose (Rest)
end
else begin
if Current = Start then
Ls^.Head := Rest;
if Current < Finish then
SublistHelper (Rest^.Next, Current + 1)
else begin
DisposeRest (Rest^.Next);
Rest^.Next := Nil;
Ls^.Size := Finish - Start + 1
end
end
end;
begin { function Sublist }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert ((1 <= Start) and (Start <= Ls^.Size) and
(1 <= Finish) and (Finish <= Ls^.Size), SublistException,
ListExceptionHandler);
if Finish < Start then begin
DisposeRest (Ls^.Head);
Ls^.Head := Nil;
Ls^.Size := 0
end
else
SublistHelper (Ls^.Head, 1);
Ls^.Cursor := Nil
end;
procedure ReverseList (var Operand: List);
var
BackLink: Link;
procedure ReverseHelper (Forwards: Link; var Backwards: Link);
var
Temporary: Element;
begin
if Forwards <> Nil then begin
Temporary := Forwards^.Datum;
ReverseHelper (Forwards^.Next, Backwards);
Backwards^.Datum := Temporary;
Backwards := Backwards^.Next
end
end;
begin { procedure ReverseList }
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
BackLink := Operand^.Head;
ReverseHelper (BackLink, BackLink);
Operand^.Cursor := Nil
end;
procedure InsertAtPositionInList (var Ls: List; Position: Integer;
Elm: Element);
procedure InsertHelper (var Rest: Link; Current: Integer);
var
OneMore: Link;
begin
if Current = Position then begin
New (OneMore);
OneMore^.Datum := Elm;
OneMore^.Next := Rest;
Rest := OneMore
end
else
InsertHelper (Rest^.Next, Current + 1)
end;
begin { procedure InsertAtPositionInList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert ((1 <= Position) and (Position <= Ls^.Size + 1),
InsertAtPositionInListException,
ListExceptionHandler);
InsertHelper (Ls^.Head, 1);
Ls^.Size := Ls^.Size + 1;
Ls^.Cursor := Nil
end;
procedure InsertAtCursorInList (var Ls: List; Elm: Element);
var
OneMore: Link;
begin
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
if Ls^.Cursor = Nil then
AppendToList (Ls, Elm)
else begin
New (OneMore);
OneMore^ := Ls^.Cursor^;
Ls^.Cursor^.Datum := Elm;
Ls^.Cursor^.Next := OneMore;
Ls^.Cursor := OneMore
end
end;
procedure DeleteAtPositionInList (var Ls: List; Position: Integer);
procedure DeleteHelper (var Rest: Link; Current: Integer);
var
OneLess: Link;
begin
if Current = Position then begin
OneLess := Rest;
Rest := Rest^.Next;
Dispose (OneLess)
end
else
DeleteHelper (Rest^.Next, Current + 1)
end;
begin { procedure DeleteAtPositionInList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert ((1 <= Position) and (Position <= Ls^.Size),
DeleteAtPositionInListException,
ListExceptionHandler);
DeleteHelper (Ls^.Head, 1);
Ls^.Size := Ls^.Size - 1;
Ls^.Cursor := Nil
end;
procedure DeleteAtCursorInList (var Ls: List);
var
OneLess: Link;
begin
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
Assert (Ls^.Cursor <> Nil, NullCursorException, ListExceptionHandler);
if Ls^.Cursor^.Next = Nil then
DeleteLastOfList (Ls)
else begin
OneLess := Ls^.Cursor^.Next;
Ls^.Cursor^ := OneLess^;
Dispose (OneLess)
end
end;
procedure DeleteValueFromList (var Ls: List; Delend: Element);
procedure DeleteHelper (var Rest: Link; var Tally: Integer);
var
OneLess: Link;
begin
if Rest <> Nil then begin
if EqualElement (Rest^.Datum, Delend) then begin
OneLess := Rest;
Rest := Rest^.Next;
Dispose (OneLess);
Tally := Tally - 1;
DeleteHelper (Rest, Tally)
end
else
DeleteHelper (Rest^.Next, Tally)
end
end;
begin { procedure DeleteValueFromList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
DeleteHelper (Ls^.Head, Ls^.Size);
Ls^.Cursor := Nil
end;
procedure ReplaceInList (var Ls: List; Displacer, Displaced: Element);
procedure ReplaceHelper (var Rest: Link);
begin
if Rest <> Nil then begin
if EqualElement (Rest^.Datum, Displaced) then
Rest^.Datum := Displacer;
ReplaceHelper (Rest^.Next)
end
end;
begin { procedure ReplaceInList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
ReplaceHelper (Ls^.Head)
end;
function FillList (Length: Integer; Filler: Element): List;
var
Filled: List;
function FillHelper (Remaining: Integer): Link;
var
Result: Link;
begin
if Remaining <= 0 then
FillHelper := Nil
else begin
New (Result);
Result^.Datum := Filler;
Result^.Next := FillHelper (Remaining - 1);
FillHelper := Result
end
end;
begin { function FillList }
Assert (0 <= Length, FillListException, ListExceptionHandler);
New (Filled);
Filled^.Size := Length;
Filled^.Head := FillHelper (Length);
Filled^.Cursor := Nil;
FillList := Filled
end;
function GenerateList (function Generator (N: Integer): Element;
Length: Integer): List;
var
Generated: List;
function GenerateHelper (Current: Integer): Link;
var
Result: Link;
begin
if Length < Current then
GenerateHelper := Nil
else begin
New (Result);
Result^.Datum := Generator (Current);
Result^.Next := GenerateHelper (Current + 1);
GenerateHelper := Result
end
end;
begin { function GenerateList }
Assert (0 <= Length, GenerateListException, ListExceptionHandler);
New (Generated);
Generated^.Size := Length;
Generated^.Head := GenerateHelper (1);
Generated^.Cursor := Nil;
GenerateList := Generated
end;
procedure TransformList (var Ls: List;
function Transformer (E: Element): Element);
procedure TransformHelper (Rest: Link);
begin
if Rest <> Nil then begin
Rest^.Datum := Transformer (Rest^.Datum);
TransformHelper (Rest^.Next)
end
end;
begin { procedure TransformList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
TransformHelper (Ls^.Head)
end;
procedure ApplyAlongList (Ls: List; procedure Applicand (E: Element));
procedure ApplyHelper (Rest: Link);
begin
if Rest <> Nil then begin
Applicand (Rest^.Datum);
ApplyHelper (Rest^.Next)
end
end;
begin { procedure ApplyAlongList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
ApplyHelper (Ls^.Head)
end;
function EveryElementOfList (Ls: List;
function Test (E: Element): Boolean): Boolean;
function EveryHelper (Rest: Link): Boolean;
begin
if Rest = Nil then
EveryHelper := True
else if Test (Rest^.Datum) then
EveryHelper := EveryHelper (Rest^.Next)
else
EveryHelper := False
end;
begin { function EveryElementOfList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
EveryElementOfList := EveryHelper (Ls^.Head)
end;
function SomeElementOfList (Ls: List;
function Test (E: Element): Boolean): Boolean;
function SomeHelper (Rest: Link): Boolean;
begin
if Rest = Nil then
SomeHelper := False
else if Test (Rest^.Datum) then
SomeHelper := True
else
SomeHelper := SomeHelper (Rest^.Next)
end;
begin { function SomeElementOfList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
SomeElementOfList := SomeHelper (Ls^.Head)
end;
procedure RecoverByTestFromList (Ls: List;
function Test (E: Element): Boolean; var Found: Boolean;
var Sought: Element);
procedure RecoverHelper (Rest: Link);
begin
if Rest = Nil then
Found := False
else if Test (Rest^.Datum) then begin
Found := True;
Sought := Rest^.Datum
end
else
RecoverHelper (Rest^.Next)
end;
begin { procedure RecoverByTestFromList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
RecoverHelper (Ls^.Head)
end;
procedure LocateByTestInList (Ls: List;
function Test (E: Element): Boolean; var Found: Boolean;
var Position: Integer);
procedure LocateHelper (Rest: Link; Current: Integer);
begin
if Rest = Nil then
Found := False
else if Test (Rest^.Datum) then begin
Found := True;
Position := Current
end
else
LocateHelper (Rest^.Next, Current + 1)
end;
begin { procedure LocateByTestInList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
LocateHelper (Ls^.Head, 1)
end;
procedure FindNextByTestInList (var Operand: List;
function Test (E: Element): Boolean);
label
99; { Exit when the test succeeds or the end of the list is reached. }
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
if Operand^.Cursor = Nil then
Operand^.Cursor := Operand^.Head
else
Operand^.Cursor := Operand^.Cursor^.Next;
while True do
if Operand^.Cursor = Nil then
goto 99
else if Test (Operand^.Cursor^.Datum) then
goto 99
else
Operand^.Cursor := Operand^.Cursor^.Next;
99:
end;
procedure FilterList (var Ls: List; function Test (E: Element): Boolean);
procedure FilterHelper (var Rest: Link; var Tally: Integer);
var
OneLess: Link;
begin
if Rest <> Nil then begin
if Test (Rest^.Datum) then
FilterHelper (Rest^.Next, Tally)
else begin
OneLess := Rest;
Rest := Rest^.Next;
Dispose (OneLess);
Tally := Tally - 1;
FilterHelper (Rest, Tally)
end
end
end;
begin { procedure FilterList }
Assert (Ls <> Nil, UninitializedListException, ListExceptionHandler);
FilterHelper (Ls^.Head, Ls^.Size);
Ls^.Cursor := Nil
end;
procedure DeallocateList (var Operand: List);
begin
Assert (Operand <> Nil, UninitializedListException,
ListExceptionHandler);
DisposeRest (Operand^.Head);
Dispose (Operand)
end;
end.
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/courses/fundamentals/cursors.html