Sequences

Sequences as an abstract data type

A sequence is a finite, linearly ordered arrangement of values of some data type. A sequence may be of any finite length, and the same value may occur more than once in a sequence, occupying different positions. The length may in particular be zero; in other words, there is an empty sequence that has no values in it. For any sequence S and any natural number k from one to the length of S, the value in the kth position in the sequence is called the kth element of the sequence, and the values in the sequence are collectively called the elements of the sequence.

The most natural sequence constructor would be a function that takes any number of values as arguments and returns a sequence containing exactly those values as elements:

make-sequence
Inputs: any number of values v1, v2, ..., vn of the element data type.
Output: result, a sequence.
Preconditions: none.
Postconditions: The length of result is equal to the number of inputs. For every natural number k from one to the number of inputs, the kth input is the kth element of result.

Unfortunately, this constructor cannot be implemented in Pascal, which does not allow programmers to define functions of variable arity. (In other words: In Pascal, every call to a programmer-defined function must have the same number of arguments.) The next best alternative is to allow sequences to be constructed element by element, starting from the empty sequence. First, we need a name for the empty sequence itself:

the-empty-sequence, the sequence that has no elements.

Then we need a function that takes an existing sequence and returns a sequence just like it except that a new element has been added at the beginning:

construct-sequence
Inputs: prefix, a value, and base, a sequence of which the elements are of the same type as prefix.
Output: result, a sequence.
Preconditions: none.
Postconditions: The length of result is one greater than the length of base. The first element of result is prefix. For every natural number k from one to the length of base, the kth element of base is the (k + 1)st element of base.

For the purpose of building up a sequence from its elements, it makes no difference whether each new element is attached at the start (the left end) of a sequence or at its finish (the right end). The implementation is perhaps somewhat more intuitive if elements are attached at the start, as described above.

The make-sequence operation can now be simulated by a succession of calls to construct-sequence, the first of which takes the-empty-sequence as its first argument.

There should be operations for recovering the inputs to construct-sequence from its output:

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

all-but-first
Input: operand, a sequence.
Output: rest, a sequence.
Preconditions: operand is not the empty sequence.
Postconditions: The length of rest is one less than the length of operand. For every natural number k from one to the length of rest, the kth element of rest is the (k + 1)st element of operand.

An operation that tests whether a given sequence is empty must be provided, so that the programmer can determine whether the precondition for either of the two preceding operations is satisfied:

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

More generally, it is often useful to know how many positions there are in a given sequence:

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

Sometimes one prefers to add or remove elements at the finish of a sequence rather than at the start:

append
Inputs: base, a sequence, and postfix, a value of the sequences's element type.
Output: result, a sequence.
Preconditions: none.
Postconditions: The length of result is one greater than the length of base. The last element of result is postfix. For every natural number k from one to the length of base, the kth element of base is the kth element of base.

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

all-but-last
Input: operand, a sequence.
Output: rest, a sequence.
Precondition: operand is not the empty sequence.
Postconditions: The length of rest is one less than the length of operand. For every natural number k from one to the length of rest, the kth element of rest is the kth element of operand.

Here are a few common operations on sequences:

To ``concatenate'' two sequences is to make a single sequence out of them, in which the elements of the second follow the elements of the first:

concatenate
Inputs: left-operand and right-operand, both sequences, with elements of the same type.
Output: result, a sequence.
Preconditions: none.
Postconditions: The length of result is the sum of the lengths of left-operand and right-operand. For every natural number k from one to the length of left-operand, the kth element of left-operand is the kth element of result. For every natural number k from one to the length of right-operand, the kth element of right-operand is the jth element of result, where j is the sum of k and the length of left-operand.

We'll want to be able to find out what value is in any given position of a sequence:

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

To check whether a particular value is in a particular sequence:

element-of
Inputs: candidate and seq, where seq is a sequence 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 seq and false if it is not.

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

locate
Inputs: sought and seq, where seq is a sequence 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 seq and found is false, or found is true and sought is the positionth element of seq and, for every natural number k from one to one less than position, sought is not the kth element of seq.

To extract a section of a sequence, starting at any given position and finishing at any given position:

subsequence
Inputs: seq, a sequence, and start and finish, both natural numbers.
Output: result, a sequence.
Precondition: Both start and finish are greater than or equal to one and less than or equal to the length of seq.
Postconditions: The length of result 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 result, the kth element of result is the jth element of seq, where j is the sum of start and k.

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

To insert a new element at any desired position:

insert-at-position
Inputs: seq, a sequence; position, a natural number; and elm, a value of the sequence's element type.
Output: result, a sequence.
Precondition: position is greater than or equal to one and less than or equal to one more than the length of seq.
Postconditions: The length of result is one more than the length of seq. elm is the positionth element of result. For every natural number k from one to the length of seq, the kth element of seq is the kth element of result if k is less than position and the (k + 1)st element of result if k is greater than or equal to position.

To remove an element from any desired position:

delete-at-position
Inputs: seq, a sequence, and position, a natural number.
Output: result, a sequence.
Precondition: position is greater than or equal to one and less than or equal to the length of seq.
Postconditions: The length of result is one less than the length of seq. For every natural number k from one to the length of result, the kth element of result is the kth element of seq if k is less than position and the (k + 1)st element of seq if k is greater than or equal to position.

To remove all occurrences of a given value from a sequence:

delete-value
Inputs: seq, a sequence, and delend, a value of the element type of seq.
Output: result, a sequence.
Preconditions: none.
Postconditions: delend is not an element of result. For every integer k from one to the length of seq, the kth element of seq is either delend or the jth element of result, 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 seq.

To substitute one value for another throughout a sequence:

substitute
Inputs: seq, a sequence, and displacer and displaced, values of the element type of seq.
Output: result, a sequence.
Preconditions: none.
Postconditions: The length of result is the length of seq. displaced is not an element of result. For every natural number k from one to the length of result, the kth element of result is displacer if the kth element of seq is displaced; otherwise, the kth element of result is the kth element of seq.

To create a sequence consisting of a certain number of copies of a given element:

fill
Inputs: length, a natural number, and filler, a value.
Output: result, a sequence 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 sequences.

generate-sequence
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 sequence 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.

map-sequence
Inputs: seq, a sequence, and mapper, an operation that takes one input, a value of the element type of seq, and yields one output.
Outputs: result, a sequence of which the elements are of the type of the output of mapper.
Preconditions: none.
Postconditions: The length of result is the length of seq. For every natural number k from one to the length of result, the kth element of result is the result of applying mapper to the kth element of seq.

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

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

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

To find the first element of a sequence that satisfies a given test:

recover-by-test
Inputs: seq, a sequence, and test, an operation that takes one input, a value of the element type of seq, and yields one output, a Boolean.
Outputs: found, a Boolean, and sought, a value of the element type of seq.
Preconditions: none.
Postconditions: Either (1) found is true and there is a natural number k, such that sought is the kth element of seq 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 seq 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 seq, applying test to the kth element of seq yields false.

To find the position of the first element of a sequence that satisfies a given test:

locate-by-test
Inputs: seq, a sequence, and test, an operation that takes one input, a value of the element type of seq, 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 seq 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 seq 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 seq, applying test to the kth element of seq yields false.

To ``filter out'' any elements of a sequence that do not satisfy a given test:

filter
Inputs: seq, a sequence, and test, an operation that takes one input, a value of the element type of seq, and yields one output, a Boolean.
Output: result, a sequence.
Preconditions: none.
Postconditions: Applying test to any element of result yields true. For any natural number k from one to the length of result, there is a natural number j such that the kth element of result is the jth element of seq 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 seq yields true.

A Sequences module in HP Pascal

Since sequences are of various lengths, it makes sense to allocate storage for them dynamically. In this implementation, a sequence is a linked structure in which each component contains one element of a sequence and a pointer to the next element, if there is one, or otherwise a Nil pointer. The empty sequence is represented simply by a Nil pointer.

It is straightforward to make Sequence an opaque type, by exporting it without exporting the SequenceRecord type that a Sequence pointer points to. This means that no operations can be performed on sequences except assignment and procedure and function call and return; it's impossible for the application programmer to break the abstraction barrier.

However, there is one serious disadvantage in using dynamic storage allocation: Somehow, the storage that is allocated in the course of the operations should be recycled, using Pascal's Dispose procedure, so that application programs do not run out of storage unnecessarily. HP Pascal does not provide a garbage collector. The next best alternative is to add to the implementation a DeallocateSequence procedure that the application programmer can and should invoke to recycle every sequence that is created. This in turn implies that every such sequence must be stored in a variable, so that the DeallocateSequence procedure can subsequently be invoked with that variable as its argument, leading to a somewhat awkward programming style in which sequences of operations that might otherwise be nested in a single expression must be separated into several statements, so that intermediate sequence values can be stored in variables.

In HP Pascal, storage is not actually recycled unless the heap_dispose compiler option is turned on, both in the module itself and in the application program that uses it.

This module makes extensive use of functional and procedural parameters, which are discussed in section 3.6 (pages 135-140) of our textbook.

Here's the source code for the module:

{ This module defines an interface for a sequence 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, 1996.
}

$heap_dispose on$

module Sequences;

$search 'elements.o'$
import Elements;

export

  type
    Sequence = ^SequenceRecord;         { an opaque type }

  const
    TheEmptySequence = Nil;

  { The ConstructSequence function takes a sequence and returns a new
    sequence just like it except that a new element has been prepended. }

  function ConstructSequence (Prefix: Element; Base: Sequence): Sequence;

  { The FirstOfSequence function returns the first element of a non-empty
    sequence. }

  function FirstOfSequence (Operand: Sequence): Element;

  { The AllButFirstOfSequence function takes a non-empty sequence and
    returns a new sequence just like it except that the first element
    has been removed. }

  function AllButFirstOfSequence (Operand: Sequence): Sequence;

  { The EmptySequence function determines whether a given sequence is the
    empty sequence. }

  function EmptySequence (Operand: Sequence): Boolean;

  { The LengthOfSequence function returns the length of a given sequence. }

  function LengthOfSequence (Operand: Sequence): Integer;

  { The AppendToSequence function takes a sequence and returns a new
    sequence just like it except that a new element has been appended. }

  function AppendToSequence (Base: Sequence; Postfix: Element): Sequence;

  { The LastOfSequence function returns the last element of a non-empty
    sequence. }

  function LastOfSequence (Operand: Sequence): Element;

  { The AllButLastOfSequence function takes a non-empty sequence and
    returns a new sequence just like it except that the last element
    has been removed. }

  function AllButLastOfSequence (Operand: Sequence): Sequence;

  { The ConcatenateSequence function takes two sequences and returns a
    new sequence in which the elements of the first operand are followed
    by the elements of the second operand. }

  function ConcatenateSequence (LeftOperand, RightOperand: Sequence):
    Sequence;

  { The RecoverByPositionFromSequence function returns the value occupying
    a specified position in a sequence. }

  function RecoverByPositionFromSequence (Position: Integer;
    Seq: Sequence): Element;

  { The ElementOfSequence function determines whether a given value is an
    element of a given sequence. }

  function ElementOfSequence (Candidate: Element; Seq: Sequence): Boolean;

  { The LocateInSequence procedure determines whether a given value is an
    element of a given sequence and, if so, returns the least position at
    which it occurs. }

  procedure LocateInSequence (Sought: Element; Seq: Sequence;
    var Found: Boolean; var Position: Integer);

  { The Subsequence function constructs and returns a sequence that is a
    copy of a section of a given sequence, bounded by the positions
    indicated by Start and Finish. }

  function Subsequence (Seq: Sequence; Start, Finish: Integer): Sequence;

  { The ReverseSequence function constructs and returns a sequence that is
    just like a given sequence except that the order of the elements is
    reversed. }

  function ReverseSequence (Operand: Sequence): Sequence;

  { The InsertAtPositionInSequence function constructs and returns a
    sequence that is just like a given sequence except that a new element
    has been inserted at a specified position. }

  function InsertAtPositionInSequence (Seq: Sequence; Position: Integer;
    Elm: Element): Sequence;

  { The DeleteAtPositionInSequence function constructs and returns a
    sequence that is just like a given sequence except that the element
    at a specified position has been removed. }

  function DeleteAtPositionInSequence (Seq: Sequence; Position: Integer):
    Sequence;

  { The DeleteValueFromSequence function constructs and returns a sequence
    that is just like a given sequence except that every occurrence of a
    specified value has been removed. }

  function DeleteValueFromSequence (Seq: Sequence; Delend: Element):
    Sequence;

  { The SubstituteInSequence function constructs and returns a sequence
    that is just like a given sequence except that every occurrence of a
    specified value has been replaced with a new specified value (the same
    one in each case). }

  function SubstituteInSequence (Seq: Sequence;
    Displacer, Displaced: Element): Sequence;

  { The FillSequence function constructs and returns a sequence consisting
    of a specified number of copies of a given element. }

  function FillSequence (Length: Integer; Filler: Element): Sequence;

  { The GenerateSequence function constructs a sequence of a specified
    length by applying a given function to the positive integers in
    ascending order until that length is reached. }

  function GenerateSequence (function Generator (N: Integer): Element;
    Length: Integer): Sequence;

  { The MapSequence function constructs a sequence by applying a given
    function to each successive element of a given sequence. }

  function MapSequence (Seq: Sequence;
    function Mapper (E: Element): Element): Sequence;

  { The ApplyAlongSequence procedure applies a given procedure to each
    successive element of a given sequence. }

  procedure ApplyAlongSequence (Seq: Sequence;
    procedure Applicand (E: Element));

  { The EveryElementOfSequence function determines whether every element
    of a given sequence satisfies a given predicate. }

  function EveryElementOfSequence (Seq: Sequence;
    function Test (E: Element): Boolean): Boolean;

  { The SomeElementOfSequence function determines whether at least one
    element of a given sequence satisfies a given predicate. }

  function SomeElementOfSequence (Seq: Sequence;
    function Test (E: Element): Boolean): Boolean;

  { The RecoverByTestFromSequence procedure determines whether any of the
    elements of a given sequence satisfies a given predicate and, if so,
    returns the one whose position is the least. }

  procedure RecoverByTestFromSequence (Seq: Sequence;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Sought: Element);

  { The LocateByTestInSequence procedure determines whether any of the
    elements of a given sequence satisfies a given predicate and, if so,
    returns the least position occupied by such an element. }

  procedure LocateByTestInSequence (Seq: Sequence;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Position: Integer);

  { The FilterSequence procedure constructs and returns a sequence
    comprising exactly those elements of a given sequence that satisfy a
    given predicate. }

  function FilterSequence (Seq: Sequence;
    function Test (E: Element): Boolean): Sequence;

  { The DeallocateSequence procedure recycles the storage associated with
    a given sequence. }

  procedure DeallocateSequence (var Operand: Sequence);

implement

import StdErr;

  const
    FirstExceptionCode = 1;

    FirstOfSequenceException = 1;
    AllButFirstOfSequenceException = 2;
    LastOfSequenceException = 3;
    AllButLastOfSequenceException = 4;
    RecoverByPositionFromSequenceException = 5;
    SubsequenceException = 6;
    InsertAtPositionInSequenceException = 7;
    DeleteAtPositionInSequenceException = 8;
    FillSequenceException = 9;
    GenerateSequenceException = 10;
    ExceptionException = 11;

    LastExceptionCode = 11;

  type
    Link = Sequence;
    SequenceRecord = record
                       Datum: Element;
                       Next: Link
                     end;

  procedure SequenceExceptionHandler (ExceptionCode: Integer);
  begin
    if (ExceptionCode < FirstExceptionCode) or
                        (LastExceptionCode < ExceptionCode) then
      ExceptionCode := ExceptionException;
    WriteLn (StdErr, 'Exception #', ExceptionCode : 1,
             ' in module Sequences:');
    case ExceptionCode of
    FirstOfSequenceException:
      WriteLn (StdErr, 'The empty sequence was passed to the ',
               'FirstOfSequence function.');
    AllButFirstOfSequenceException:
      WriteLn (StdErr, 'The empty sequence was passed to the ',
               'AllButFirstOfSequence function.');
    LastOfSequenceException:
      WriteLn (StdErr, 'The empty sequence was passed to the ',
               'LastOfSequence function.');
    AllButLastOfSequenceException:
      WriteLn (StdErr, 'The empty sequence was passed to the ',
               'AllButLastOfSequence function.');
    RecoverByPositionFromSequenceException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'RecoverByPositionFromSequence function.');
    SubsequenceException:
      WriteLn (StdErr, 'An invalid index was passed to the Subsequence ',
               'function.');
    InsertAtPositionInSequenceException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'InsertAtPositionInSequence function.');
    DeleteAtPositionInSequenceException:
      WriteLn (StdErr, 'An invalid index was passed to the ',
               'DeleteAtPositionInSequence function.');
    FillSequenceException:
      WriteLn (StdErr, 'A negative Length argument was passed to the ',
               'FillSequence function.');
    GenerateSequenceException:
      WriteLn (StdErr, 'A negative Length argument was passed to the ',
               'GenerateSequence function.');
    ExceptionException:
      WriteLn (StdErr, 'The SequenceExceptionHandler procedure received ',
               'an unknown exception code.')
    end
  end;

  function CopySequence (Operand: Sequence): Sequence;
  var
    Result: Sequence;
  begin
    if Operand = TheEmptySequence then
      CopySequence := TheEmptySequence
    else begin
      New (Result);
      Result^.Datum := Operand^.Datum;
      Result^.Next := CopySequence (Operand^.Next);
      CopySequence := Result
    end
  end;

  function ConstructSequence (Prefix: Element; Base: Sequence): Sequence;
  var
    Result: Sequence;
  begin
    New (Result);
    Result^.Datum := Prefix;
    Result^.Next := CopySequence (Base);
    ConstructSequence := Result
  end;

  function FirstOfSequence (Operand: Sequence): Element;
  begin
    Assert (Operand <> TheEmptySequence, FirstOfSequenceException,
            SequenceExceptionHandler);
    FirstOfSequence := Operand^.Datum
  end;

  function AllButFirstOfSequence (Operand: Sequence): Sequence;
  begin
    Assert (Operand <> TheEmptySequence, AllButFirstOfSequenceException,
            SequenceExceptionHandler);
    AllButFirstOfSequence := CopySequence (Operand^.Next)
  end;

  function EmptySequence (Operand: Sequence): Boolean;
  begin
    EmptySequence := (Operand = TheEmptySequence)
  end;

  function LengthOfSequence (Operand: Sequence): Integer;
  begin
    if Operand = TheEmptySequence then
      LengthOfSequence := 0
    else
      LengthOfSequence := 1 + LengthOfSequence (Operand^.Next)
  end;

  function AppendToSequence (Base: Sequence; Postfix: Element): Sequence;
  var
    Result: Sequence;
  begin
    New (Result);
    if Base = TheEmptySequence then begin
      Result^.Datum := Postfix;
      Result^.Next := TheEmptySequence
    end
    else begin
      Result^.Datum := Base^.Datum;
      Result^.Next := AppendToSequence (Base^.Next, Postfix)
    end;
    AppendToSequence := Result
  end;

  function LastOfSequence (Operand: Sequence): Element;
  begin
    Assert (Operand <> TheEmptySequence, LastOfSequenceException,
            SequenceExceptionHandler);
    if Operand^.Next = TheEmptySequence then
      LastOfSequence := Operand^.Datum
    else
      LastOfSequence := LastOfSequence (Operand^.Next)
  end;

  function AllButLastOfSequence (Operand: Sequence): Sequence;
  var
    Result: Sequence;
  begin
    Assert (Operand <> TheEmptySequence, AllButLastOfSequenceException,
            SequenceExceptionHandler);
    if Operand^.Next = TheEmptySequence then
      AllButLastOfSequence := TheEmptySequence
    else begin
      New (Result);
      Result^.Datum := Operand^.Datum;
      Result^.Next := AllButLastOfSequence (Operand^.Next);
      AllButLastOfSequence := Result
    end
  end;

  function ConcatenateSequence (LeftOperand, RightOperand: Sequence):
    Sequence;
  var
    Result: Sequence;
  begin
    if LeftOperand = TheEmptySequence then
      ConcatenateSequence := CopySequence (RightOperand)
    else begin
      New (Result);
      Result^.Datum := LeftOperand^.Datum;
      Result^.Next :=
                ConcatenateSequence (LeftOperand^.Next, RightOperand);
      ConcatenateSequence := Result
    end
  end;

  function RecoverByPositionFromSequence (Position: Integer;
    Seq: Sequence): Element;
  var
    Length: Integer;

    function RecoverHelper (Rest: Sequence; Current: Integer): Element;
    begin
      if Current = Position then
        RecoverHelper := Rest^.Datum
      else
        RecoverHelper := RecoverHelper (Rest^.Next, Current + 1)
    end;

  begin { function RecoverByPositionFromSequence }
    Length := LengthOfSequence (Seq);
    Assert ((1 <= Position) and (Position <= Length),
            RecoverByPositionFromSequenceException,
            SequenceExceptionHandler);
    RecoverByPositionFromSequence := RecoverHelper (Seq, 1)
  end;

  function ElementOfSequence (Candidate: Element; Seq: Sequence): Boolean;
  begin
    if Seq = TheEmptySequence then
      ElementOfSequence := False
    else if EqualElement (Seq^.Datum, Candidate) then
      ElementOfSequence := True
    else
      ElementOfSequence := ElementOfSequence (Candidate, Seq^.Next)
  end;

  procedure LocateInSequence (Sought: Element; Seq: Sequence;
    var Found: Boolean; var Position: Integer);

    procedure LocateHelper (Rest: Sequence; Current: Integer);
    begin
      if Rest = TheEmptySequence 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 LocateInSequence }
    LocateHelper (Seq, 1)
  end;

  function Subsequence (Seq: Sequence; Start, Finish: Integer): Sequence;
  var
    Length: Integer;

    function CopySection (Rest: Sequence; Position: Integer): Sequence;
    var
      Result: Sequence;
    begin
      if Position < Start then
        CopySection := CopySection (Rest^.Next, Position + 1)
      else if Position <= Finish then begin
        New (Result);
        Result^.Datum := Rest^.Datum;
        Result^.Next := CopySection (Rest^.Next, Position + 1);
        CopySection := Result
      end
      else
        CopySection := TheEmptySequence
    end;

  begin { function Subsequence }
    Length := LengthOfSequence (Seq);
    Assert ((1 <= Start) and (Start <= Length) and
            (1 <= Finish) and (Finish <= Length), SubsequenceException,
            SequenceExceptionHandler);
    Subsequence := CopySection (Seq, 1)
  end;

  function ReverseSequence (Operand: Sequence): Sequence;

    function AccumulateReverseSequence (SoFar, Rest: Sequence): Sequence;
    var
      OneMore: Sequence;
    begin
      if Rest = TheEmptySequence then
        AccumulateReverseSequence := SoFar
      else begin
        New (OneMore);
        OneMore^.Datum := Rest^.Datum;
        OneMore^.Next := SoFar;
        AccumulateReverseSequence :=
                        AccumulateReverseSequence (OneMore, Rest^.Next)
      end
    end;

  begin { function ReverseSequence }
    ReverseSequence :=
                AccumulateReverseSequence (TheEmptySequence, Operand)
  end;

  function InsertAtPositionInSequence (Seq: Sequence; Position: Integer;
    Elm: Element): Sequence;
  var
    Length: Integer;

    function InsertHelper (Rest: Sequence; Current: Integer): Sequence;
    var
      Result: Sequence;
    begin
      New (Result);
      if Current = Position then begin
        Result^.Datum := Elm;
        Result^.Next := CopySequence (Rest);
      end
      else begin
        Result^.Datum := Rest^.Datum;
        Result^.Next := InsertHelper (Rest^.Next, Current + 1)
      end;
      InsertHelper := Result
    end;

  begin { function InsertAtPositionInSequence }
    Length := LengthOfSequence (Seq);
    Assert ((1 <= Position) and (Position <= Length + 1),
             InsertAtPositionInSequenceException,
             SequenceExceptionHandler);
    InsertAtPositionInSequence := InsertHelper (Seq, 1)
  end;

  function DeleteAtPositionInSequence (Seq: Sequence; Position: Integer):
    Sequence;
  var
    Length: Integer;

    function DeleteHelper (Rest: Sequence; Current: Integer): Sequence;
    var
      Result: Sequence;
    begin
      if Current = Position then
        DeleteHelper :=  CopySequence (Rest^.Next)
      else begin
        New (Result);
        Result^.Datum := Rest^.Datum;
        Result^.Next := DeleteHelper (Rest^.Next, Current + 1);
        DeleteHelper := Result
      end
    end;

  begin
    Length := LengthOfSequence (Seq);
    Assert ((1 <= Position) and (Position <= Length),
            DeleteAtPositionInSequenceException,
            SequenceExceptionHandler);
    DeleteAtPositionInSequence := DeleteHelper (Seq, 1)
  end;

  function DeleteValueFromSequence (Seq: Sequence; Delend: Element):
    Sequence;
  var
    Result: Sequence;
  begin
    if Seq = TheEmptySequence then
      DeleteValueFromSequence := TheEmptySequence
    else if EqualElement (Seq^.Datum, Delend) then
      DeleteValueFromSequence :=
                        DeleteValueFromSequence (Seq^.Next, Delend)
    else begin
      New (Result);
      Result^.Datum := Seq^.Datum;
      Result^.Next := DeleteValueFromSequence (Seq^.Next, Delend);
      DeleteValueFromSequence := Result
    end
  end;

  function SubstituteInSequence (Seq: Sequence;
    Displacer, Displaced: Element): Sequence;
  var
    Result: Sequence;
  begin
    if Seq = TheEmptySequence then
      SubstituteInSequence := TheEmptySequence
    else begin
      New (Result);
      if EqualElement (Seq^.Datum, Displaced) then
        Result^.Datum := Displacer
      else
        Result^.Datum := Seq^.Datum;
      Result^.Next :=
                SubstituteInSequence (Seq^.Next, Displacer, Displaced);
      SubstituteInSequence := Result
    end
  end;

  function FillSequence (Length: Integer; Filler: Element): Sequence;

    function FillHelper (Remaining: Integer): Sequence;
    var
      Result: Sequence;
    begin
      if Remaining <= 0 then
        FillHelper := TheEmptySequence
      else begin
        New (Result);
        Result^.Datum := Filler;
        Result^.Next := FillHelper (Remaining - 1);
        FillHelper := Result
      end
    end;

  begin { function FillSequence }
    Assert (0 <= Length, FillSequenceException, SequenceExceptionHandler);
    FillSequence := FillHelper (Length)
  end;

  function GenerateSequence (function Generator (N: Integer): Element;
    Length: Integer): Sequence;

    function GenerateHelper (Current: Integer): Sequence;
    var
      Result: Sequence;
    begin
      if Length < Current then
        GenerateHelper := TheEmptySequence
      else begin
        New (Result);
        Result^.Datum := Generator (Current);
        Result^.Next := GenerateHelper (Current + 1);
        GenerateHelper := Result
      end
    end;

  begin { function GenerateSequence }
    Assert (0 <= Length, GenerateSequenceException,
            SequenceExceptionHandler);
    GenerateSequence := GenerateHelper (1)
  end;

  function MapSequence (Seq: Sequence;
    function Mapper (E: Element): Element): Sequence;
  var
    Result: Sequence;
  begin
    if Seq = TheEmptySequence then
      MapSequence := TheEmptySequence
    else begin
      New (Result);
      Result^.Datum := Mapper (Seq^.Datum);
      Result^.Next := MapSequence (Seq^.Next, Mapper);
      MapSequence := Result
    end
  end;

  procedure ApplyAlongSequence (Seq: Sequence;
    procedure Applicand (E: Element));
  begin
    if Seq <> TheEmptySequence then begin
      Applicand (Seq^.Datum);
      ApplyAlongSequence (Seq^.Next, Applicand)
    end
  end;

  function EveryElementOfSequence (Seq: Sequence;
    function Test (E: Element): Boolean): Boolean;
  begin
    if Seq = TheEmptySequence then
      EveryElementOfSequence := True
    else if Test (Seq^.Datum) then
      EveryElementOfSequence := EveryElementOfSequence (Seq^.Next, Test)
    else
      EveryElementOfSequence := False
  end;

  function SomeElementOfSequence (Seq: Sequence;
    function Test (E: Element): Boolean): Boolean;
  begin
    if Seq = TheEmptySequence then
      SomeElementOfSequence := False
    else if Test (Seq^.Datum) then
      SomeElementOfSequence := True
    else
      SomeElementOfSequence := SomeElementOfSequence (Seq^.Next, Test)
  end;

  procedure RecoverByTestFromSequence (Seq: Sequence;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Sought: Element);
  begin
    if Seq = TheEmptySequence then
      Found := False
    else if Test (Seq^.Datum) then begin
      Found := True;
      Sought := Seq^.Datum
    end
    else
      RecoverByTestFromSequence (Seq^.Next, Test, Found, Sought)
  end;

  procedure LocateByTestInSequence (Seq: Sequence;
    function Test (E: Element): Boolean; var Found: Boolean;
    var Position: Integer);

    procedure LocateHelper (Rest: Sequence; Current: Integer);
    begin
      if Rest = TheEmptySequence then
        Found := False
      else if Test (Rest^.Datum) then begin
        Found := True;
        Position := Current
      end
      else
        LocateHelper (Rest^.Next, Current + 1)
    end;

  begin { procedure LocateByTestInSequence }
    LocateHelper (Seq, 1)
  end;

  function FilterSequence (Seq: Sequence;
    function Test (E: Element): Boolean): Sequence;
  var
    Result: Sequence;
  begin
    if Seq = TheEmptySequence then
      FilterSequence := TheEmptySequence
    else if Test (Seq^.Datum) then begin
      New (Result);
      Result^.Datum := Seq^.Datum;
      Result^.Next := FilterSequence (Seq^.Next, Test);
      FilterSequence := Result
    end
    else
      FilterSequence := FilterSequence (Seq^.Next, Test)
  end;

  procedure DeallocateSequence (var Operand: Sequence);
  begin
    if Operand <> TheEmptySequence then begin
      DeallocateSequence (Operand^.Next);
      Dispose (Operand)
    end
  end;

end.

This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/courses/fundamentals/sequences.html

created July 29, 1996
last revised August 2, 1996

John David Stone (stone@math.grin.edu)