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