Lists

Lists as a container type

We've considered sequences as composite, immutable data structures. But it is also possible to think of a linearly ordered structure of finite but variable size as a container for data -- a mutable object that preserves a kind of identity as its contents change. The term list is often used for such an object.

All of the operations proposed for sequences have analogues for lists. However, whereas many of the operations take sequences as inputs and yield newly constructed sequences as outputs, the corresponding list operations modify the contents of the list. For instance, the insert-at-position operation on a list will change the list it is given as input instead of creating an entirely new object to output. In the Pascal implementation, this operation is a procedure (with a variable parameter of type List) rather than a function.

Since the identity of a list is independent of its contents, a program may use several lists that all happen to be empty at the same time; so it is inappropriate to speak of ``the'' empty list. Instead of a constant, we have a function that creates and returns a new list, initially empty:

make-empty-list
Inputs: none.
Output: result, a list.
Preconditions: none.
Postcondition: The length of result is 0.

The analogue of construct-sequence changes an existing list instead of creating a new one:

prepend
Inputs: prefix, a value, and base, a list of which the elements are of the same type as prefix. Outputs: none.
Preconditions: none.
Postcondition: The length of base at output is one greater than its length at input. The first element of base at output is prefix. For every natural number k from one to the length of base at input, the kth element of base at input is the (k + 1)st element of base at output.

There is an operation to get the first element of a list, and another that undoes the effect of prepend:

first
Input: operand, a list.
Output: first, a value of the list's element type.
Preconditions: operand is not empty.
Postcondition: first is the first element of operand.

delete-first
Input: operand, a list.
Outputs: none.
Preconditions: operand is not empty.
Postconditions: The length of operand at output is one less than the length of operand at input. For every natural number k from one to the length of operand at output, the kth element of operand at output is the (k + 1)st element of operand at input.

To determine whether a given list is empty:

empty-list
Input: operand, a list.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if operand is empty and false if it is not.

To compute the length of a list:

length
Input: operand, a list.
Output: result, a natural number.
Preconditions: none.
Postconditions: result is the number of positions in operand.

Again, there are operations analogous to prepend, first, and delete-first, but taking place at the finish of the list:

append
Inputs: postfix, a value, and base, a list of which the elements are of the same type as prefix. Outputs: none.
Preconditions: none.
Postconditions: The length of base at output is one greater than the length of base at input. The last element of base at output is postfix. For every natural number k from one to the length of base at input, the kth element of base at output is the kth element of base at input.

last
Input: operand, a list.
Output: last, a value of the list's element type.
Preconditions: operand is not empty.
Postcondition: last is the last element of operand.

delete-last
Input: operand, a list.
Outputs: none.
Preconditions: operand is not empty.
Postconditions: The length of operand at output is one less than the length of operand at input. For every natural number k from one to the length of operand at output, the kth element of operand at output is the kth element of operand at input.

Sometimes even the user a list package wants to use a ``non-destructive'' version of some of these operations. The simplest approach is to provide a way to make an exact copy of a given list, in completely separate storage, and then to perform the ``destructive'' operation on the copy.

copy-list
Input: operand, a list.
Output: result, a list.
Preconditions: none.
Postconditions: operand is not the same list as result. The length of operand is the length of result. For every natural number k from one to the length of operand, the kth element of operand is the kth element of result.

Here are the analogues of the remaining sequence operations:

concatenate
Inputs: left-operand and right-operand, both lists, with elements of the same type.
Outputs: none.
Preconditions: none.
Postconditions: The length of left-operand at output is the sum of the lengths of left-operand at input and right-operand. For every natural number k from one to the length of left-operand at input, the kth element of left-operand is the kth element of left-operand at output. For every natural number k from one to the length of right-operand, the kth element of right-operand is the jth element of left-operand at output, where j is the sum of k and the length of left-operand at input.

recover-by-position
Inputs: position, a natural number, and ls, a list.
Output: result, a value of the list's element type.
Precondition: position is greater that or equal to one and less than or equal to the length of ls. (Note that this implies that ls is not the empty list.)
Postcondition: result is the positionth element of ls.

Since lists are mutable, one common operation is to store a new value at one particular position in the list:

assign-at-position
Inputs: position, a natural number; ls, a list; and new-value, a value of the list's element type. Outputs: none.
Precondition: position is greater that or equal to one and less than or equal to the length of ls. Postcondition: The length of ls at output is 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 new-value if k is position; otherwise, it is the kth element of ls at input.

element-of
Inputs: candidate and ls, where ls is a list and candidate is a value of its element type.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if candidate is an element of ls and false if it is not.

To find the first position at which a particular value occurs in a particular list:

locate
Inputs: sought and ls, where ls is a list and sought is a value of its element type.
Outputs: found, a Boolean, and position, a natural number.
Preconditions: none.
Postconditions: Either sought is not an element of ls and found is false, or found is true and sought is the positionth element of ls and, for every natural number k from one to one less than position, sought is not the kth element of ls.

sublist
Inputs: ls, a list, and start and finish, both natural numbers.
Outputs: none.
Precondition: Both start and finish are greater than or equal to one and less than or equal to the length of ls at input.
Postconditions: The length of ls at output is 0 if finish is less than start; otherwise, it is one greater than the difference between finish and start. For every natural number k from one to the length of ls at output, the kth element of result is the jth element of ls at input, where j is the sum of start and k.

reverse
Input: operand, a list.
Outputs: none.
Preconditions: none.
Postcondition: The length of operand at output is the length of operand at input. For every natural number k from one to the length of operand at input, the kth element of operand at input is the jth element of operand at output, where j is the difference between one more than the length of operand at input and k.

insert-at-position
Inputs: ls, a list; position, a natural number; and elm, a value of the list's element type.
Outputs: none.
Precondition: position is greater than or equal to one and less than or equal to one more than the length of ls.
Postconditions: The length of ls at output is one more than the length of ls at input. elm is the positionth element of ls at output. 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 k is less than position and the (k + 1)st element of ls at output if k is greater than or equal to position.

delete-at-position
Inputs: ls, a list, and position, a natural number.
Outputs: none.
Precondition: position is greater than or equal to one and less than or equal to the length of ls at input.
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 position and the (k + 1)st element of ls at input if k is greater than or equal to position.

delete-value
Inputs: ls, a list, and delend, a value of the element type of ls.
Outputs: none.
Preconditions: none.
Postconditions: delend is not an element of ls at output. For every integer k from one to the length of ls at input, the kth element of ls at input is either delend or the jth element of ls at output, where j is the number of natural numbers i such that i is greater than or equal to one, i is less than or equal to k, and delend is not the ith element of ls at input.

replace
Inputs: ls, a list, and displacer and displaced, values of the element type of ls.
Outputs: none.
Preconditions: none.
Postconditions: The length of ls at output is the length of ls at input. displaced is not an element of ls at output. For every natural number k from one to the length of ls at output, the kth element of ls at output is displacer if the kth element of ls at input is displaced; otherwise, the kth element of ls at output is the kth element of ls at input.

fill
Inputs: length, a natural number, and filler, a value.
Output: result, a list of which the element type is the type of filler. Preconditions: none.
Postconditions: The length of result is length. For every natural number k from one to length, the kth element of result is filler.

The following meta-operations take operations as inputs as apply them either to create or to obtain values from lists.

generate-list
Inputs: generator, an operation that takes a positive integer as its only input and yields one output, and length, a natural number.
Output: result, a list of which the elements are of the type of the output of generator.
Preconditions: none.
Postconditions: The length of result is length. For every natural number k from one to length, the kth element of result is the result of applying generator to k.

transform-list
Inputs: ls, a list, and transformer, an operation that takes one input, a value of the element type of ls, and yields one output of the same type.
Outputs: none.
Preconditions: none.
Postconditions: The length of ls at output is 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 result of applying transformer to the kth element of ls at input.

apply-along-list
Inputs: ls, a list, and applicand, an operation that takes one input, a value of the element type of ls and yields no outputs.
Outputs: none.
Preconditions: none.
Postcondition: For every natural number k from one to the length of ls, the operation applicand has been applied to the kth element of ls. (If the same value occurs in more than one position in ls, applicand is applied to it as many times as it occurs.)

every-element
Inputs: ls, a list, and test, an operation that takes one input, a value of the element type of ls, and yields one output, a Boolean.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is false if there is an element e of ls such that applying test to e yields false. Otherwise, result is true.

some-element
Inputs: ls, a list, and test, an operation that takes one input, a value of the element type of ls, and yields one output, a Boolean.
Output: result, a Boolean.
Preconditions: none.
Postconditions: result is true if there is an element e of ls such that applying test to e yields true. Otherwise, result is false.

recover-by-test
Inputs: ls, a list, and test, an operation that takes one input, a value of the element type of ls, and yields one output, a Boolean.
Outputs: found, a Boolean, and sought, a value of the element type of ls.
Preconditions: none.
Postconditions: Either (1) found is true and there is a natural number k, such that sought is the kth element of ls and applying test to sought yields true, while, for any natural number j greater than or equal to one and less than k, applying test to the jth element of ls yields false; or (2) found is false and, for any natural number k greater than or equal to one and less than or equal to the length of ls, applying test to the kth element of ls yields false.

locate-by-test
Inputs: ls, a list, and test, an operation that takes one input, a value of the element type of ls, and yields one output, a Boolean.
Outputs: found, a Boolean, and position, a natural number.
Preconditions: none.
Postconditions: Either (1) found is true and applying test to the positionth element of ls yields true, while, for any natural number k greater than or equal to one and less than position, applying test to the kth element of ls yields false; or (2) found is false and, for any natural number k greater than or equal to one and less than or equal to the length of ls, applying test to the kth element of ls yields false.

filter
Inputs: ls, a list, and test, an operation that takes one input, a value of the element type of ls, and yields one output, a Boolean.
Outputs: none.
Preconditions: none.
Postconditions: Applying test to any element of ls at output yields true. For any natural number k from one to the length of ls at output, there is a natural number j such that the kth element of ls at output is the jth element of ls at input and there are exactly k natural numbers i greater than or equal to one and less than or equal to j such that applying test to the ith element of ls at input yields true.

A Lists module in HP Pascal

Like a sequence, a list is most appropriately implemented as a dynamically allocated structure. In this implementation, I've chosen to attach a header to the linked structure developed in the Sequences module. A header is a structure that contains a pointer to the first component of the linked structure as one of its fields, but also has one or more other fields containing identification of or summary information about the object it heads. In this case, the header contains a Size field that indicates how many components the list currently has. It's useful to save this theoretically superfluous information, since many of the frequently invoked list operations begin by checking the length of an argument list, and it's inefficient to recompute this length every time by traversing the list and tallying the components.

Here is the Lists module:

{ This module defines an interface for a list data type 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 2-5, 1996.
}

$heap_dispose on$

module Lists;

$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 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 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 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 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 DeleteAtPositionInList procedure removes the element at a specified
    position in a list. }

  procedure DeleteAtPositionInList (var Ls: List; Position: Integer);

  { 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 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;
    RecoverByPositionFromListException = 6;
    AssignAtPositionInListException = 7;
    SublistException = 8;
    InsertAtPositionInListException = 9;
    DeleteAtPositionInListException = 10;
    FillListException = 11;
    GenerateListException = 12;
    ExceptionException = 13;

    LastExceptionCode = 13;

  type
    Link = ^ListComponent;
    ListComponent = record
                      Datum: Element;
                      Next: Link
                    end;
    ListRecord = record
                   Size: Integer;
                   Head: 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.');
    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;
    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
  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)
  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)
  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)
  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);
    CopyList := Result
  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)
  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;

  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;

  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 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)
  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)
  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
  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
  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)
  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);
    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);
    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 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)
  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/lists.html

created March 14, 1996
last revised October 18, 1996