Principles of Computer Programming - A Mathematical Approach
Principles of Computer Programming - A Mathematical Approach
1988
Victor R. Basili
John D. Gannon
Richard D. Hamlet
Recommended Citation
Mills, Harlan D.; Basili, Victor R.; Gannon, John D.; and Hamlet, Richard D., "Principles of Computer Programming: A Mathematical
Approach" (1988). The Harlan D. Mills Collection.
http://trace.tennessee.edu/utk_harlan/7
This Book is brought to you for free and open access by the Science Alliance at Trace: Tennessee Research and Creative Exchange. It has been accepted
for inclusion in The Harlan D. Mills Collection by an authorized administrator of Trace: Tennessee Research and Creative Exchange. For more
information, please contact trace@utk.edu.
conditional assignment
(EOLN -+Number := 1 0 1 ) (NOT(EOLN) -+ Number,Nc := m mod 10, c)
where m is the length of the future string of INPUT up to the next line marker, and c is the last
haracter before that line marker . (The effect of Count on INPUT is ignored here .)
Substituting f for ICounii in the recurrence equation, consider two cases, an empty and
onempty future INPUT strmg up to the first line marker.
Case 1: L is empty. Then for state s, the left side of the recurrence equation in f is
!( s) = t, where t is the same as s except that the value of Number is the character
representing 0 mod 10, i.e ., 0.
The right side is covered by the first set, in which IEOLNj( s) = TRUE, and in that set s is paired
ith this same t. That is, in Case 1 f satisfies the recurrence equation.
Case 2: L is nonempty. Let the length of L be k > 0, and let its last character be c. Then for
~ate s, the left side of the recurrence equation in f is
!( s) = t, where
t is the same as s except that the value of Number is the representative for k
mod 10, and the value of Nc is c.
I
~ e rig_ht side is covered by the second set, in which EOLNj{ s) =FALSE, and there the value paired
t h SIS
t = (I READ (Nc) lo f o INextDigi t (Number) j)(s).
~ can be worked out using trace tables.
CASE 2.1
Part Condition IN 2 Nc Number
L \l I
READ(Nc) L\ll # tt A(L \1 I) e(L \lI)
f e(A(L \lI))= I c 0
NextDigit ... 1
e(A(L \l I)) =I
::-aluates to TRUE. Also, since L is a 1-string, e(L \l I) = c is the only character in L (before the
· e marker). Thus, ignoring the effects on INPUT, the conditional assignment is
(length (L) = 1 -+ Number, Nc := 1 1 1 , c)
CASE 2.2
Part Condition IN 2 Nc Number
L\lj
READ(Nc) (L \lI)# tt A(L \l /) 8(£ \lI)
J (e(A(L \l /))) # I c length(A(L)) mod 10
NextDigi,t ... (length(A(L)) mod 10 + 1)
mod 10
_ extDigi t increments Number except for value 9, which becomes 0. This behavior is addition
.::nodulo 10.)
Chapter Preview
The techniques of program design, and verification that a design is correct, can be formalized.
The specification for a program, a statement of the problem and what the program should do,
plays the central role in this formalization . Careful mathematical formalism plays a role even
in large designs where full details are impractical to obtain.
Preview
A program specification describes a problem and its solution as a relation between input and
output strings.
New ideas: specification.
Any program has a purpose. Usually, that purpose is to solve some problem. Data for the
_ oblem will be given in the form of an input character string, and the solution will be described by
"ving the output string the execution of the program should produce. These input and output
::-rings may contain words and numbers which are meaningful to the problem solver but which are
:-"'a.d and written as strings of characters, one at a time, by the program . A program specification for
- problem defines the set of input strings which represent meaningful data for the problem and for
,:oa.ch input string defines the set of (one or more) output strings which are acceptable solutions for
- a.t problem statement. Thus a specification is a relation consisting of all pairs of acceptable
t ances of input data and output data for a problem . The domain of this relation is all acceptable
puts. For character strings not in the domain of this relation, there is no required program
- havior.
Here are three examples:
A specification for sorting character strings. The input is any character string. For each input
string only one output string is acceptable, namely the character string with exactly the same
elements as the input but in sorted order . The specification will be a function in this case
because every input defines a unique output.
A specification to evaluate an arithmetic expression written with natural numbers. Most
character strings are not allowed as inputs, only those that represent the arithmetic
expressions with natural-number ope rands. (These might be precisely described using BNF,
much as the <expression>s of OF Pascal are described .) The output string must be a
representation of a single natural number, which is the value of the evaluated input expression.
Unless there is latitude given about how the result is to appear in the string, its base, etc., this
specification is a function.
A specification to evaluate a character set expression. The input will be only those strings
representing set expressions, with the set elements listed explicitly. The output is to represent
the result of evaluating the expression . The output may not be unique because the elements of
sets can be list ed in different orders, so a set with two or more elements can be represented by
more t han one character string. In this case the specification is a relation that isn't a function .
..,...hese three examples illustrate that program specifications may be functions or relations and their
omains may be restricted.
CR. The <word>s of <Wlist> must be the same as those of <WBlist>, in the same order.
The BNF desc ription is very precise and formal, but the context r ule could be improved . The
appropriate level of formality depends upon the knowledge and understanding of the specification's
reader, and its importance to the people for whom the problem is being solved. One way to make
the description of the words and their order more formal is to define a function that maps the words
of a string to a list of words, preserving the order. Call this function words. For example:
words(t Thi s is time is t) = <tTh ist, t i st, ttime t, ti s t>.
When we want to refer to all the possible strings that are defined by a BNF rule, we use the rule's
name, for example we talk about a <block> in CF Pascal. Then the input for the problem of
removing extra blanks is a <Fileln> sand the output is a <FileOut> 5 , related by the context rule
CR. words( <Fileln> 5 ) = wo rds( <FileOut> 5 ),
wit h the meaning that the strings on which words operates are those descr ibed by a ny < Fileln> s
12.1.2 Exercises
12.1.1 Write a program specification for addition of several natural numbers, where the problem is
presented in infix notation. Describe the syntax of input and output strings using BNF, and define a
function number that maps strings representing numbers to the numbers. Use these elements in the
specification.
12.1.2 Write a program specification for printing all four-letter words found in a character string, in
t he same order as found.
12.1.3 Write a program specification for the problem of Sara Revere's Letter from Section 2.3.3.
12.1.4 Write a program specification for the problem of the Towers of Hanoi from Section 11.3.1.
12.1.5 Given a program P, we have learned in the previous chapters how to calculate ~.
Comment on the following statement: Since P has been written to solve some problem, and smce
[!j is a function, 0should be used for the specification of that problem.
12.1.6 Criticize the following as a specification of sorting characters from one file into another,
where duplicate characters of the input are to occur just once in the output:
The output file is to be a sequence of characters such that each character in the sequence
comes strictly after the one before it in alphabetical order .
Give a more precise specification for this problem.
12.1.7 Some people believe that specifications should never be relations that are not functions,
because a function better pins down exactly what is to be done . Give a brief argument against this
position.
12.1.8 Specifications often have "don't care" parts. For some inputs, the output is of no interest, so
-he author of the specification, if questioned about what should be done for those inputs, answers "I
don't care." But how should this be reflected in the specification relation? Two possibilities suggest
hemselves: (1) the relation should include all possible values for output paired with that input; or,
(2) the relation should include no pairs with that input. Explain which you think is a better
definition to take.
12.1.9 Describe a problem whose specification relation might be said to be a "specification for the
programming language CF Pascal." What are the input values, and what are the outputs? This
specification should certainly include the BNF description of CF Pascal. What is missing if only the
BNF were used?
Preview
Formally, correctness is a property of the progr am function and the specification relation, a
property that can be proved using set theory. Effort should be split wisely between program
design a nd program prov ing. The usual mistake is a too ambitious design that gets out of
i intellect ual control.
12.2.3 Exercises
12.2.1 Explain why the following conditions are not satisfactory as definitions for program
correctness:
a)rn0=r
b) range( r
c)rn0= P
nt) = range( r)
d) domain( r n p ) = domain( 0)
12.2.2 Give a formal definition of "program Pis correct with respect to specification r at input x. "
Show that your definition, prefaced with "for every x," is equivalent to the one given in this section.
12.2.3 When a programmer writing program P "omits a case" in solving a problem, because he or
she did not think of the possibility of some input value x, what does this mean about pairs in !Plwith
input value x? Suppose that in fact the specification relation for P does not include any su~pairs
with input x. If the program has no other mistakes, explain why it is then correct, using the
definition of this section.
12.2.4 A particular specification includes some "don't-care" inputs, pairing them with all possible
output values. Explain why it is still possible to write a program that is not correct for one of the
don't-care inputs.
Preview
------------------------------------------------------------------------------------
Program parts may be given specifications, which may be conditional assignments. Then a
program part can be proved correct . The design of a program using stepwise composition is a
process of choosing a function, then refining it into simpler functions that compose it. These
functions are the specifications to which program parts must be written.
The program calculus has been developed for CF Pascal programs in complete detail, but to
handle large program designs its level of formality is too high. A way is needed to scale up to large
programs without losing the mathematical discipline of designing to precise functions. Because the
calculus works with sets and functions, these can be described less formally, yet still maintain the
design discipline.
12.3.4 Exercises
12.3.1 Are the mappings that go with conditional assignments ever relations that are not functions?
Explain .
12.3.2 When conditional assignments are used in designs as comments, the design could be executed
if correct actual Pascal statements replaced the comment. Could this be done automatically? That
is, could a program be written to take as input a design containing conditional-assignment
comments, and create as output correct Pascal?
a) Give a specification for such a program, using the box notation.
b) Explain why such a program cannot be written .
Data Abstractions
Systematic program development in CF Pascal is accomplished through the stepwise refinement of
programs. The program calculus and design rules derived from it provide a mathematical basis for
writing correct programs in CF Pascal. In Part IV, these design concepts are extended to a more
powerful idea, namely that of high-level data and operations in a more powerful language called D
(Design) Pascal.
The strategy of stepwise refinement is to solve a problem by defining a small program whose
parts are to be designed later. Each part could be later designed as a procedure; the term procedural
abstraction describes the stepwise-refinement breakdown into subtasks. In any case, whether
designed as a Pascal procedure or not, the subprogram implementing the subtask will be a program
part with a program part function. Indeed, in stepwise refinement, a program part function is
defined to meet the need of the problem being solved, and then a program part, which will have
exactly this function, is designed later.
The new design concept in Part IV is data abstraction. Whereas a procedure abstraction can be
defined by a single Pascal procedure, a data abstraction must be defined by a system of procedures
and data declarations, in such a way that these procedures provide the only means of access to this
data. Such a system of declarations and procedures is called a program module. The advantage of
data abstraction over procedure abstraction is that it can be used to retain data for later use,
rather than just making calculations as a procedure abstraction does. Files are one data abstraction
built into Pascal. Predefined statements (RESET, READ, etc .) manipulate files declared as variables.
These statements can be used to store and retrieve data, and the collection of operations and a file
declaration defines that file as a data abstraction.
Data abstraction is a more powerful design concept than procedure abstraction because the
underlying mathematical idea is a state machine r ather than a function . A function always produces
the same output when presented with the same input:
input
----~·~~~---f_u_n_c_t-io_n--~r---~~~ output
A state machine can accept an input, produce an output depending on the input and its persistent
internal state, and possibly change that internal state:
transition
input output
function
persistent
data
For example, with a file, consecutive READ statements do not produce the same response. In t he fi le
da ta abstraction, the internal state of the data abstraction will be the file value: a string with
cursor and the current file access mode.
PROGRAM MODULES
Chapter Preview
The most difficult part of programming is maintaining intellectual control over the emerging
program, as more and more details are incorporated into it. To maintain control, it is essential
that parts of the program be designed and put away, so that they need not be examined in
detail again. The program module is a mechanism for encapsulating program ideas in just this
way. Several example modules, one to manipulate a counter, others to maintain a queue, will be
developed in this chapter. A module differs from a procedure in that it can retain information
between successive operations it performs. In this regard it behaves like a formal object called a
state machine.
The strategy of stepwise program refinement is to design any program as a hierarchy of design
steps, which define tasks, then expand tasks into design parts to meet the task requirements. Each
expansion of a task is a program part which will be specified in terms of the part function it must
satisfy. This function describes the data transformation performed by the program part.
The main idea in this chapter is to extend the concept of a program part to a new, more
powerful design construct, the program module. A program module, or more simply a module, is
defined to be a group of declarations of data and procedures which are specified in a single block of a
program with the following restrictions:
Data declared with the module will be accessed only by the statements (in the procedures) of
the module (and by no statements outside the module).
The statements (in the procedures) of the module will access only parameters and data of the
module (but no other data outside the module).
A program module provides data storage, processing, and retrieval service for the rest of the
program. An illustration of such a service is provided by the declaration of TEXT files and the
READ and WRITE statements which manipulate TEXT files. These statements provide the only
way to store and retrieve data in the files. Communication between the module and the remainder
of the program is provided by the character parameters of the READ and WRITE statements.
The definition of a program module is not explicitly part of the syntax of Pascal, any more
than a design part in stepwise refinement is. Rather, it provides another design discipline for
organizing programs in an additional, complementary way to stepwise refinement .
The module restrictions permit the module to protect its data from all other parts of the
program; that is, once the procedures of the module have been carefully worked out, there will be no
danger of the module's data being inadvertently altered or destroyed. This method of defensive
programming is called information hiding, and the data is said to be encapsulated in the module .
This discipline forces all communication with a module to take place through its procedures
and their parameters. Therefore, it is not possible for any data in the module to be altered except
by a procedure statement that names a procedure of the module. Nor is it possible for such a
procedure statement to affect any data outside the module except through the parameters.
A module with only pure storage and retrieval is an important special case. Data, enters the
module through procedure parameters, and may later be returned unchanged through other
parameters. CF Pascal files exemplify this property: the data put into a file always reappears in
exactly the same form when it is taken out .
Another important special case of a module is one which has no data declarations, in which
pure computation is carried out . A procedure using no global variables satisfies the restrictions all
by itself. A group of procedures that form a library, for example, those for natural-number
processing suggested in Chapter 9, could constit ute a pure-computation module.
13.1 Numbers
Preview
To illustrate the idea of a module, a three-digit counter will be developed and used.
END; {Start}
Bump
in sequence? Explain why or why not.
c) Write the full text of BumpTen without usmg Bump, and show how it is to be incorporated
into the module .
13 .1.3 Can a module procedure be recursive without violating the access rules?
13.1.4 Suppose a module has just one procedure . Can the data storage for the module be
incorporated into the procedure as local variables? Explain .
13.1.5 Create a switch module with variable:
VAR
Switch: CHAR;
and procedures:
PROCEDURE Set;
{set switch to down}
PROCEDURE Flip;
{if the switch is down, set it to up
if the switch is up, set it to down}
PROCEDURE Value(VAR Sw: CHAR);
{Sw := 'T' if switch is up, 'F' if down}
13.1.6 Use the switch module of the previous exercise to create a program that cop1es the odd
characters of one line of input to the output.
13.1.7 Use the counter module in writing a program to count the number of reversals in a character
string. A reversal is three successive characters such that the middle character is either greater or
less than the other two characters .
13.1.8 Suppose a certain module contains the following:
VAR
Safe: CHAR;
{declare other variables of the module here}
FUNCTION Check(VAR OK: CHAR);
BEGIN {Check}
IF Safe = 'Y'
THEN
OK .- 'T'
ELSE
OK .- 'F I
END; {Check}
{other functions and procedures of the module}
Users of this mof:lule are expected to write code like:
3 .2 Data Abstractions
Preview
A module may be thought of as introducing a whole new level of objects into a program- the
abstract level. In abstract terms, the module contains new, higher-level objects than were
available before it was written, and when these objects are used (through the module's
operations), none of the lower-level details need be considered.
ause of the data-access discipline in a module, its procedure part functions can be determined
_ -i:rely from the module itself wit hout reference to the data declared outside. However, two
- ""erent views need to be considered- those of the modu le's implementor and those of its users. To
r k on a module itself, an implementor needs comments that describe the effect of the module's
~ ocedures on its hidden data (which is sometimes called its concrete data). Since the users of a
='Xlule cannot reference this hidden data, comments written for them, explaining how to use an
=... eration, must be written in terms of the abstract data provided by the module. In the counter
-odule, the implementor's comments would describe the effect of each procedure in terms of Ones,
= ens , and Hundreds, while the users' comments wou ld detail the effects of the same operations
=.:o:ing an abstract object called Counter . Counter is not like the other variables in the state in
-_at it was not declared and cannot be directly used in Pascal statements. Instead, the programmer
t he value of Counter indirectly via Start and Bump, and accesses its value using Va l ue.
For example, the original Start comment:
{reset counter to zero}
_:m be replaced by the more precise comments:
{concrete: Ones,Tens , Hun dred s : = '0', '0', ' 0'}
{abstract: Counter : = 0}
-:..e first comment is a concurrent assignment that summarizes actual CF Pascal statements. But
- e second comment uses a variable that is thought of as a natural number,- with a natural-number
3.lue, not available in CF Pascal. Implementors think in the former terms, users in the latter.
The value of an abstract object can be constructed by mapping the values of the variables used
· _ r epresent t he object t o values of the object. This mapping is part of a representation function
-- at maps (concrete) sta tes to (abst ract) states. A representation function extracts an abstract
alue from a state and hides the component variables that comprise an abstract object (which is
::arned by a new identifier). For example, in CountingBlanksinText, the following states might
:.x:ist after the call to Value:
Preview
he action of a program module is like a formal machine with the property that its responses to
· put depend on its internal state. As each operation is done, the internal state may change,
and thus past actions can influence future ones.
A state machine is a mechanism (real or imagined) that contains a distinguished state called
"' start state, can accept an input, then produce a new state and an output which depend only on
-"' previous state and input. That is, a state machine is defined by a function that maps a
_put, state) pair into an (output, state) pair. For example, a TEXT file behaves as a state machine
which the state is the value of the file, and the inputs and outputs are defined by READ and
·"RITE statements. Taking a file Fl in execution state s with the value:
!Flj(s) = <tABt, tent, R>
d the statement READ (F 1, Ch) as input to the TEXT-file machine, the result IS a new state t
-~ h that
One state of the machine is designated as the start state-it will have an unlabeled arc coming
into it that does not originate at another state. For example, S is a start state in:
Several states of a finite state machine may be final states-they are represented by double circles.
The same state can be both the start state and a final state as in the following sample machine.
X I#
A module with only CHAR internal data, like the counter module, can be modeled by a finite
~a.te machine. A module with TEXT internal data cannot be modeled by a finite state machine
cause there is no bound on the length of a TEXT file, and hence no bound on the number of ·
~-ossible states. Finite state machines are extremely useful for describing situations in which a fixed
ber of choices arise from input strings of arbitrary length. For example, a finite state machine
be used to determine if an input string of characters contains an ascending run of length at least
The state can be the last 9 characters. There are a finite number of such states because the
ber of possible characters is itself limited. As each input character arrives; the decision can be
a.de that the 9 saved characters plus the arrival do or do not make up the run of 10. If they do the
er is yes; if no run has yet occurred and the string ends, the answer is no. The new state
= a ins the newly arrived character and drops the oldest character. On the other hand, a finite
e machine cannot be used to determine if a string is a palindrome by keeping the first .half of the
:ing as a state. No matter how large a state is defined, an even larger string may be used as input.
A finite state machine can also help guide program testing. At the minimum, each transition
- uld be explored once by test data. The following BEGIN statement achieves this goal for the
ter module.
BEGIN {test counter module}
Start;
Value(Chl,Ch2,Ch3);
WRITELN(Chl,Ch2,Ch3);
WHILE (Chl<>'9' OR Ch2 <>'9' OR Ch3<>'9')
DO
BEGIN
Bump;
Value(Chl,Ch2,Ch3);
WRITELN(Chl,Ch2,Ch3)
END;
Bump;
Value(Chl,Ch2,Ch3);
WRITELN(Chl,Ch2,Ch3)
END {test counter module}
_:is test tries all the counter values and the transition to the next, but it omits an unlimited
her of t ransitions t hat the mod ule can make, namely from t he 999 state to itself; the test tries
one such t ra nsition . Still, if the output has been carefully observed, this seems a good test of
module.
Preview
The queue is a useful data structure for holding information in an ordered sequence, much the
way people stand in a line. A queue of characters will be made into a module, implemented in
two different ways, and used to solve the problem of removing extra blanks from text.
A variable of type TEXT provides a facility for storing and retrieving characters in a sequenc
using the special syntax of READ /WRITE statements. TEXT variables and operations observe th
rules of a program module, because the only way to store or retrieve data in a file is through READ
and WRITE.
A queue is a similar storage-and-retrieval facility. It operates first-in, first-out (FIFO
elements are added to the rear of a queue and retrieved from the front of a queue . Thus, a queue ~
like a file except that additions and deletions may be interspersed with one another. For example
consider a queue whose elements are characters. A typical set of operations includes:
Operation Effect
EmptyQ initialize queue to empty
AddQ(VAR Elt: CHAR) append El t value to queue
DelQ remove first element of queue
HeadQ(VAR Elt: CHAR) set E l t to value of queue's first element
If the queue contains one or more elements, DelQ removes the first from the queue and Headx.
returns the first element in the queue by copying it into E l t (but does not alter the queue). If th"'
queue is empty, DelQ has no effect and HeadQ returns the character #.
In the following sections two module implementations are given for a queue of characters. T h-
first implementation, called S l owQueue, is based on a very simple data representation which d
not permit much optimization or efficiency in the procedures of the program modu le. The secon-
implementation (FastQueue) uses a more complex data representation and more comple
3.4.1 SlowQueue
? or a first implementation of a queue consider a strategy of maintaining the queue in a single text
- e named Q:
VAR
Q: TEXT
empty queue is represented as a file containing a single line marker. Since the end of queue is
arked in this way, no queue can contain internal line breaks. Each procedure ends with .a RESET
- t he file so that when any procedure starts execution it can assume the file is ready far reading.
_ dding an element to, or deleting an element from the queue, requires WRITE statements. Since the
-:-EXT file representing the queue is open for reading, the current contents of the file must be copred
- a temporary file and then back to the original file. The outline of the queue module with abstract
mments is shown below.
{queue module}
VAR
Q: TEXT;
{include PROCEDURE CopyOpen(VAR Fin, Fout: TEXT)
Append the future string of Fin to Fout.}
PROCEDURE EmptyQ;
{Queue := < >}
PROCEDURE AddQ(VAR Elt: CHAR);
{Queue := Queue & <Elt>}
PROCEDURE DelQ;
{(Queue=<> --> )
(Queue= <X>&Y -->Queue := Y )}
PROCEDURE HeadQ(VAR Elt: CHAR);
{(Queue=<>--> Elt := ' # ' ) I
(Queue= <X>&Y --> Elt :=X)}
{end queue module}
The concrete functions corresponding to these abstract functions will now be implemented.
~ptyQ rewrites Q as a single empty line. In the concrete comment, / is used to indicate the end of
line.
PROCEDURE EmptyQ;
{Q := <,/,R>}
BEGIN {EmptyQ}
REWRITE(Q);
WRITELN(Q);
RESET(Q)
END {EmptyQ}
AddQ copies Q to a temporary file and appends the contents of El t. Then the temporary file
-- copied back to Q.
13.4.2 FastQueue
In SlowQueue, AddQ and DelQ do too much file copying, degrading the performance of a user's
program. The number of copies can be reduced in two ways: (1) using two files and a switch to
remember which file holds the current contents of the queue frees us from copying the updated file
back into the original file; and (2) using a last-operation indicator permits sequences of just AddQs
or just DelQs to be performed without file copies.
The underlying data structure to support this more complex operation must now be more than
just the file Q of the slow version:
VAR
Ql, Q2: TEXT;
Switch, LastOp: CHAR;
Either Ql (if Switch has the value 1) or Q2 (if Switch has the value 2) holds the contents of
the queue. To add an element to the queue whose contents are in the TEXT file Ql, the contents of
Ql are copied to Q2, a new element is appended to Q2, and Switch is assigned 2 to indicate that
Q2 rather than Ql holds the contents of the queue. A similar sequence of operations can be used to
add an element to Q2. Thus, the AddQ and DelQ operations now need only one copy operation
instead of the two copy operations that were needed in the slower implementation.
Consecutive applications of either AddQ or DelQ offer another opportunity to improve the
performance of this implementation. AddQ copies one file to another and appends the value to the
new file . Instead of resetting the new file, AddQ can leave it open for writing. If the next operation
is also an AddQ , the new value can be written on the end of the open file. If an AddQ is followed by
any other operation, an end marker is written on the file holding the queue's values and the file is
reset before the new operation begins. Similarly, successive DelQ operations can be implemented by
continuing to read from the same file . When an operation other than DelQ follows a DelQ, the
part of the file remaining to be read is copied to the other file before the new operation bep:ins. (The
LastOp
A AddQ
D DelQ
E EmptyQ
H HeadQ
w WriteQ
The operations for this faster version of queue are much more complicated than the
corresponding ones for the slow version because the operations use two files instead of one to hold the
queue values and these files are not always reset at the end of an operation. EmptyQ, HeadQ, and
Wr i teQ reset the file holding the queue's values so that at the end of one of these operations the file
has the form:
<tt,x V /,R> where x is a string.
Since AddQ leaves the file open for writing, the file has the form:
<x,tt,w> where xis a string.
After DelQ, part of the file may have already been read. This operation leaves the file in the form:
<x,y V /,R> where x and y are strings.
Each of these possibilities must be considered in writing the operations for the que.ue.
EmptyQ arbitrarily chooses Q1 to hold the initial value of the queue.
PROCEDURE EmptyQ;
{Q1,Switch,Last0p := <,/,R>, '1', 'E'}
BEGIN {EmptyQ}
REWRITE(Q1);
WRITELN(Q1);
RESET(Q1);
Switch:= '1';
LastOp := 'E'
END {EmptyQ}
Although Wri teQ is merely a testing aid, not part of the final queue module, it will be
designed next. The reason is twofold: it will help in testing the other procedures to be developed;
and, to write it requires a solid understanding of the representation being used for the queue. In
designing Wri teQ, the major obstacle to be faced is that the file containing the queue's values can
be in any one of three states:
1. <x,y V /,R> (partially read) if the last operation was DelQ .
2. <tt,y V /,R> (ready to read) if the last operation was Wr i teQ, HeadQ, or EmptyQ.
3. <y,tt,w> (partially written) if the last operation was AddQ.
Converting cases 1 and 3 to case 2 greatly simplifies the design task. Since the other queue
operations have similar problems, two procedures, CloseDel and CloseAdd, are introduced .
CloseDel converts case 1 to case 2, if necessary, and CloseAdd converts case 3 to case 2, if
necessary. Therefore, Wri teQ begins with CloseDel and CloseAdd. Once the file containing
the queue's values is in a state corresponding to case 2, CopyOpen can be used to write the values
from Q1 or Q2 to OUTPUT.
Del(Q1,Q2);
Design Part 4.2.2
{copy Q2 to Q1 removing first element}
Del(Q2,Q1);
Design Part 4.2.3
PROCEDURE Del(VAR QFrom, QTo: TEXT);
{copy QFrom to QTo removing first element}
VAR
Ch: CHAR;
BEGIN {Del}
RESET(QFrom);
REWRITE(QTo);
READ(QFrom,Ch);
CopyOpen(QFrom,QTo);
WRITELN(QTo);
RESET(QTo)
END {Del}
The structure of the fast queue module is shown below. The procedures Head, Add, Del, and
.?eadOne are hidden from the user by nesting their declarations inside the declarations of other
:=' ocedures. CloseAdd and CloseDel must remain exposed since Wri teQ, HeadQ, AddQ, and
:>elQ must all be able to invoke them .
{Include PROCEDURE CopyOpen(VAR Fin, FOut: TEXT)}
PROCEDURE EmptyQ;
PROCEDURE CloseAdd;
PROCEDURE CloseDel;
PROCEDURE WriteQ(VAR FOut: TEXT);
PROCEDURE HeadQ(VAR Elt: CHAR);
PROCEDURE Head(VAR Q: TEXT; VAR Ch: CHAR);
PROCEDURE AddQ(VAR Elt: CHAR);
PROCEDURE Add(VAR QFrom, QTo: TEXT;
VAR Elt: CHAR);
PROCEDURE DelQ;
PROCEDURE Del(VAR QFrom, QTo: TEXT);
PROCEDURE ReadOne(VAR Q: TEXT);
13.4.4 Exercises
13.4.1 Suppose that the operations DelQ and HeadQ were combined in PullQ:
PROCEDURE PullQ(VAR Elt: CHAR)
{Remove first element of queue, and
return its value in Elt}
a) Is this formulation of a queue equivalent to the that of Section 13.4, in the sense that anything
one can do, the other can do? Explain.
b) Suppose that PullQ is to be added to the queue module as another operation. Give the
shortest implementation you can for it.
c) Explain why an implementor who decided to replace DelQ and HeadQ with PullQ, would
not be observing the spirit of the rules for constructing modules.
d) Suppose that an implementation includes PullQ, but not DelQ or HeadQ. Can a module
user (not implementor) obtain the exact actions of the latter operations in terms of what exists
in the implementation? Explain why not, or else give the statements that will do the job.
13.4.2 Compare the performance of SlowQueue and FastQueue when included m
RemoveExtraBlanks, for the input shown at the end of Section 13.4.3. Count the number of
r ead/write operations in each case.
13.4.3 Describe a situation in which FastQueue would be slower than SlowQueue . (Hint: m
what sequence of operations would all the tricks FastQueue uses be to no avail?)
13.4.4 Using an endline marker in RemoveExtraBlanks makes is impossible to handle strings
t hat include this marker as a character.
a) Explain why the endline marker cannot be eliminated by using a test for end of file.
b) Is there a way to avoid wasting a character as an endline marker?
13.4.5 Give an implementation of a queue of characters that holds the queue in five CHAR
variables, and limits the queue size to five.
13.4.6 Use a queue module to write a procedure that reverses a TEXT file. Use diagrams and
English description to explain the idea you plan to use, and then do a careful design by stepwise
refinement.
Preview
Queues are not the only useful, simple structures that can profitably be implemented as
modules. Sets, stacks, and relations are others.
There a re several simple storage and retrieval structures that can be implemented directly
with modules: sets, stacks, priority queues, relations, and functions.
13.5.1 Exercises
13.5.1 Discuss the ideas needed to add, remove, and test for elements in a set-of-characters module ,
implemented as a TEXT file in the order the characters are added, with
a) duplicates eliminated in the file;
b) duplicates entered in the file.
13.5.2 Create and test a module for strings with the following procedures:
PROCEDURE MakeString(VAR Raw: TEXT)
{Beginning at the current input position in Raw,
create the string from the characters up to but
not including the first $ character.
MakeString is to be undefined if no $ occurs
in the future string of Raw. Leave Raw positioned
following the $ .}
PROCEDURE LeadingUpto(VAR Marker: CHAR)
{Replace the string with its shortest initial
substring that ends just before Marker; if no
Marker value appears, leave the string unchanged.}
PROCEDURE EndingWith(VAR Marker: CHAR)
{Replace the string with the longest substring
beginning with Marker and extending to the end;
if Marker does not appear, replace the string with
the empty string.}
PROCEDURE HeadString(VAR HChar: CHAR)
{If the string has a length of at least 1,
return its first character in HChar, and
remove the first character, reducing its length by 1.
If the string is empty, neither the string nor
HChar should change and an error message
should be printed.}
PROCEDURE TailChar(VAR TChar: CHAR)
{Replace the string with another formed by
adding TChar as an additional, last character.}
Chapter Preview
D Pascal extends CF Pascal largely by adding new kinds of objects, data types in addition to
characters and files. The ordinal types are those in which the objects are simple rather than
compound-each object is an entity, not composed of other objects. An enumerated type
contains a fixed, finite collection of named objects. The BOOLEAN type has just the two objects
TRUE and FALSE. The INTEGER type is an approximation to the positive and negative whole
numbers. Finally, a portion of a type can be made a type in its own right, a subrange type. Of
course, the types include operations that define what can be done with the objects. For
BOOLEAN, INTEGER, etc., these operations are well known intuitively.
Data types describe sets of values and the operations that can be applied to them. CF Pascal
introduced the data types CHAR and TEXT . The values of type CHAR are the set of legal
characters of the Pascal machine and the operations on these values are the relational operators:
< > <= >= <>
whose meanings are the corresponding mathematical operations, with the inequalities signifying
alphabetical sequence. The values of the data type TEXT are sequences of lines, each a sequence of
characters. The operations on these values are:
RESET REWRITE READ WRITE WRITELN EOF EOLN
Data types offer the advantages of abstraction, replication, and authentication. Data typ e~
abstract important properties of data. For example, to understand the comparison
'A' < 'B'
it is not necessary to know how the characters are represented on the Pascal machine-the collatina
sequence defines the meaning of this operation. Users need not be concerned with the number of biL
used to represent a character value, what is done with any extra bits in a word, whether the bi
pattern for B is exactly one greater than that for A, and so on.
Modules achieve a kind of data abstraction, but data types offer something more. Declaratioll5
of variables of a data type permit the storage and manipulat ion of any number of values of the ty pe
through these variables. Each use of a variable or constant in a program is authenticated to ensure
that only proper operations are applied to it. The context of an operand in a program implies t he
type of the operand; this implied type is redundant information that can be checked against t he
declared type of the operand to prevent operations on values of the wrong type. Consider t he
following program fra gment:
PROGRAM Typex(INPUT,OUTPUT);
VAR
Ch: CHAR;
F: TEXT;
BEGIN {Typex}
IF Ch = F THEN
END. {Typex}
Since = is only defined for character operands in CF Pascal, the appearance of Ch and F as iL
operands implies that both must have values of type CHAR. While Ch is declared to have the righ-
type, F is declared as TEXT so :'en inc0nsistcrcy ·s detected.
T2 = Tl;
h Tl and T2 are type identifiers and may be used in the current block to declare variables and
rm al parameters just as CHAR is used.
Preview
T he sit ua tion ofte n arises t hat a small collection of values is needed, each value having a
mnemonic name. Instead of using single-character constants, Pascal programmers may employ
a rbitrary identifiers for these constants.
In the declarations:
TYPE
DayOfWeek = (Sunday, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday);
WeekEndDay = (Saturday, Sunday); {not legal}
VAR.
Friday: CHAR; {not legal}
the identifiers of the type WeekEndDay and the variable Friday are illegal duplicates of t he
identifiers Friday, Saturday, and Sunday that appear first in DayO fWeek. That is, identifiers
must be unique over all their uses as names of types, variables, procedures, or as constants in a
enumerated type.
Enumerated types are the "supercharacters" of Pascal. They have precisely the same uses ~
ordinary characters and the same operations. But programmers can invent any number of the m
with identifiers that spell out the interpretation of their values. In earlier chapters we used CHAR
constants for this purpose, but enumerated types are better. For example, instead of a procedure
returning Y or N to indicate success, and enumerated type with identifiers Yes and No could he
used, or even one with identifiers Success and F ai 1 ure .
Since the letters of the alphabet are identifiers, they can be used as to define a data type:
TYPE
Alphabet = (A,B,C,D,E,F,G,H,I,J,K,L,M,
N,O,P,Q,R,S,T,U,V,W,X,Y,Z);
VAR.
Letter: Alphabet;
This permits statements such as:
IF Letter = A
THEN
Letter := E
but not:
IF Letter = 'A'
THEN
Letter := 'E'
since A and E are constants of type Alphabet, agreeing with the type of Letter, but 'A' and
'E' are of type CHAR, not the same as t he type of Letter .
14.1.4 Exercises.
14.1.1 Give a syntax tree for each of the following that is a <type definition>, and for the others
explain why each is not.
a)TYPE Swit.ch = (On, Off);
b) TYPE Swi.tch Value = (On, Off) ;
c)TYPE Weekend= (Dayl, Day2);
d)TYPE Weekend= (lstday, 2ndday);
14.1.2 Consider a program containing the following type declarations:
TYPE
WeekDay= (Monday, Tuesday, Wednesday, Thursday, Friday);
WeekEnd= (Saturday, Sunday);
Analyze the expression:
a)Friday <Wednesday
b)Friday <Monday
c)Monday < Saturday
14.1.3 Is the following type definition legal? What would be the consequences of permitting
enumerated types to be defined in this way?
TYPE
NewAlphabet = ( 'Z' , 'Y' , 'X' , 'W' , 'V' , 'U' , 'T' , 'S' , 'R' ,
'Q', 'P', '0', 'N', 'M', 'L', 'K', 'J', 'I',
'H' , 'G' , 'F' , 'E' , 'D' , 'C' , 'B' , 'A')·,
14.1.4 Create a module for
TYPE Switch= (On, Off);
with operations SetOn, SetOff, Flip, and Value and specifications to match the procedure
names. Provide complete instructions for an intended user of the module.
14.1.5 Create an input/output module for enumerated type Color.
TYPE
Color= (Red, Blue, Yellow, Green, Orange);
14.1.6 Consider a set of traffic signals at an intersection and a procedure that examines their values
and determines if they represent a safe combination. Use the following defi!J-itions and declarations.
Preview
The Boolean operations of NOT, AND, and OR were introduced in Chapter 3. These operations
along with variables form the type BOOLEAN of D Pascal. By giving the syntax for Boolean
expressions in a somewhat different way, the associativity and precedence of the operators can
be specified.
The BOOLEAN type uses the constant values FALSE and TRUE and the operators NOT, AND,
OR, and the relationals. The two BOOLEAN constant identifiers act as though they had been
declared in the enumerated type declaration:
TYPE
BOOLEAN= (FALSE, TRUE);
(But this declaration is not given explicitly.) Thus relational operators are defined on Boolean
operands so that the value of
FALSE < TRUE
is TRUE. NOT is a unary prefix operator and AND and OR are binary infix operators. The
' unctions computed by AND, OR , and NOT are described in Section 3.3.
Pascal guarantees the order in which operators are applied, but not that each operator is
?-PPlied. Many actual Pascal machines perform "lazy" evaluations of Boolean operators. If the first
operand of an OR has value TRUE, the second operand need not be evaluated because
TRUE OR x =TRUE
no matter what x may be. Similarly, if the first operand of an AND has value FALSE, the second
operand need not be evaluated because the value of the entire expression will be FALSE. This
method of evaluation may cause expressions which would otherwise be undefined to have a value .
Boolean variables can be declared and can be assigned values computed with Boolean
expressions. For example, given the declaration:
VAR
EndWord: BOOLEAN;
t he following Pascal statement assigns either the value TRUE or FALSE to EndWord:
EndWord := (Ch = '#') OR (Ch = ' ')
<expression>
------------- ~
<simple expression>
\
\
<relational operator> <simple expression>
\
\
\
\
\
\
<= <term>
\
\
\
\
\
\
?? <factor>
<variable access>
z
It is impossible to derive ·the string X <= Y from the first <simple expression> without introducing
extra characters into the string (e .g., parentheses). However, the othe r expression can be derived:
<simple expression>
<term>
I
/'\-----___
<term> AND <factor>
/
<factor>
/'\-----___ ( <expression> )
( <expression>
\
\
\
\
\
---- )
''
''
y <=
''
' z
X <= y
Successive left-associative operators of the same precedence m an expressiOn are applied to
• eir operands in left-to-right order. Thus in the expression:
X OR Y OR Z
• e sub-expression:
X OR Y
ev aluated first. The syntax tree for this expression is:
<term>
// /
""
<term>
I
<factor>
OR <factor>
<variable access>
<variable access>
z
~
<variable access> y
X
The operands for each OR in the string may be found by locating the strings derived from the
corresponding <simple expression> and <term>. The leftmost OR has operands X and Y, wh ile
the rightmost OR has operands X OR Y (i.e ., the string derived from the first <simple expression>
and Z. Thus the leftmost OR is applied to its operands before the rightmost one. (Of course, the
precedence of operators in an expression can be altered with parentheses.)
A rule of syntax is left recursive if the quantity being defined (i.e., the one to the left of ::=) also
appears as the first symboi to the right of ::=. The rule for <term> is left recursive:
<term> ::= <term> AND <factor>
When an operator is defined using left recursion, it will be left associative as shown by th
arrangement of its syntax trees.
The relative precedence of operators is also defined by the rules of syntax. The syntax tree fo~
X OR Y AND Z illustrates precedence:
I
~X~
<simple ex~ <a/erator> <t;i.m>
<factor>
<term>
<term>
OR
~~
<mult operator> <factor>
/
"'
<variable access>
X
<factor>
"'
<variable access>
/
AND
/
<variable access>
y
Despite being the rightmost operator in the string, AND can be applied to its operands, Y and Z,
::>efore OR can be applied to its operands, X and Y AND Z. Thus AND has a highe"r precedence
- an OR in expressions.
4.2.5 Exercises
A .2.1 Draw syntax trees for each of the following, making corrections so that each is syntactically
- rrect . In each case give the order in which the operations are performed, and the declarations
=.eeded to make the phrase correct.
a)NOT NOT A
b)A :=NOT B > C
c) Qu AND (A < B)
A.2.2 Given declarations
TYPE
Light= (Red, Yellow,Green);
VAR
North, East, South, West: Light;
n.ich describe conditions of traffic lights at an intersection, create a Boolean expression which
_- aluates to TRUE for safe conditions.
:.4.2.3 Create a window data type with operations Ini tWindow, MoveWindow, and
::l.SpectWindow so that the SarahRevere program in Section 14.2.4 could be written as shown
- low.
BEGIN {SarahRevere}
{Initialize Window, Looking, Land, Sea}
InitWindow;
Looking : = TRUE;
Land : = FALSE;
Sea := FALSE
WHILE Looking AND NOT(Land OR Sea)
DO
BEGIN
{move window, check end of data}
MoveWindow;
InspectWindow(Wl,W2,W3,W4);
Looking:= W4 <> '#';
{check window for 'land'}
Land := (Wl='l') AND (W2='a') AND
(W3='n') AND (W4= 'd');
{check window for 'sea'}
Sea := (Wl='s') AND (W2= ' e ' ) AND (W3='a')
END;
BEGIN {Create Sarah's message}
END
END. {SarahRevere}
Preview
The INTEGER data type simplifies programming tasks in which counting is useful. All the usual
arithmetic operations are available. Arithmetic using INTEGER operands is fast because most
computers have special support for this data type. However, the payment for this speed is a
limitation on the magnitude of the integer values that may occur.
The INTEGER data type has values that are positive and negative whole numbers and zero.
Each Pascal machine restricts INTEGER values to a subset of the integers:
{-MAX/NT, ... , -2, -1, 0, 1, 2, .. .,MAX/NT}
MAX/NT is a predefined integer constant (typically a power of two) corresponding to the larges
integer value that can be represented on a particular Pascal machine. Variables declared to have
type INTEGER can be assigned any of these values:
VAR
I, J: INTEGER
The INTEGER operators are the relationals and:
+ * DIV MOD
for addition, subtraction, multiplication, division with truncation, and remainder on division.
Addition, subtraction, and multiplication are subject to overflow (see below), but DIV and MOD are
not. The operators + and - can be used both as unary prefix and binary infix operators. Thus the
expression J - I could be written - I + J. Division with truncation usually rounds toward zero
so that for example:
[n the expression I DIV J the result is undefined if the value of J is zero, and zero if the absolute
alue of I is less than the absolute value of J. Otherwise, the sign of the result is positive if I and
have the same sign, and negative if they have different signs.
The result of the expression I MOD J is undefined if the value of J is less than or equal to
ero; otherwise it is the remainder on dividing the value of I by the value of J. For example:
Operation Value
5 MOD 3 2
-7 MOD 3 -1
Expression Evaluation
-7 - ( -7 DIV 3 * 3) start with expression in parentheses
-7 - (-2 * 3) DIV has the same precedence as *
-7 - (-6) evaluate expression in parentheses
-1
the execution of the READ statement will produce a state with partial contents:
{Il·-12, I2·3, I3·32}.
When reading a TEXT file containing an unknown number of integers, the final line marker rema~
after the last integer is read. Thus EOF cannot be used as a test for termination, as the prograE
Readints mistakenly attempts to do:
14.3.3 Overflow
Pascal INTEGER operations are only guaranteed to produce a correct result if all the valu -
involved lie within the range [MAXINT, MAXINT]. That is, the operands must be in range, and t -
result of the operation as well. For the addition operation, the figure shows the region of correc-
results for X + Y within the dashed lines.
MAXINT
r------------------
''
''
''
''
''
''
''
''
''
''
''
-MAX/NT (0, 0) '' MAXINT
''
' X
''
''
''
''
''
''
''
''
''
''
''
'' -MAXINT
''
' __________________ J
Outside the boundaries defined by MAX/NT, addition does not produce correct results. Starting
wit h operands chosen at random within range, there is 1 chance in 4 that the result wi'lL overflow
1/4 of the area bounded by ±MAX/NT in the figure represents error results).
Similarly, the region for correct results for subtraction X - Y is show-n in the figure:
MAXINT
, , ------------------,
, ,,
,
, ,,
,
, ,,
,
, ,,
,
, ,,
,
-MAX/NT , ,, (0, 0) MAX/NT
,,
, , X
,,
, ,,
, ,,
,
, ,,
,
, ,,
,
, ,,
,
,,
,,
L------------------
-MAX/NT
Again there is probability .25 that overflow will occur in subtracting two random INTEGER values.
Since multiplication can generate large values more easily than addition or subtraction, t h
overflow problem is even more dramatic for this operation. The region of correct results fo
multiplication X*Y is shown below by the solid lines:
4
I
MAX/NT
I
MAX/NT
X
-MAX/NT
-MAX/NT
The curved boundaries are hyperbolas, and the figure distorts the scale to show unit distance from
each axis. In fact the area of correct results is surprisingly small. In one quadrant, the area under
~h e curve Y =MAX/NT /X is given by:
MAXINT
MAX/NT + I MAX/NT dX
1 X
=MAX/NT + MAX/NT ln MAX/NT- MAXINT ln 1.
bus the area under the curve is MAXINT + MAXINT ln MAXINT, while the entire area IS
_ fAXINT2, so the chance of randomly selecting two operands for which overflow will not occur is:
MAXINT + MAXINT lnMAXINT 1 + lnMAXINT
MAXINT2
MAXINT
or MAXINT=109, the chance of correct results from the multiplication of values chosen at random
f'rom type INTEGER is
1 + ln 109 ,......, 50
109 "" 109 '
about 1 in 20 million.
In cont rast to addition, subtraction, and multiplication, the division operators DIV and MOD
~an not cause overflow.
8)(8) =- ~(S)
!]ill (S) = ~ (S)
The definition gives the meaning of each operator. For an expression with more than one operator,
·he meaning is built up term by term, according to Pascal's syntax rules for precedence and
associativity.
14.3.7 Exercises
14.3.1 What is the value of MAX/NT in your Pascal machine? What happens when you try to read
a. value larger than MAX/NT from · the input? What happens when you try to write a constant
arger than MAX/NT as a part of a Pascal program? What happens when you try to add two
· t egers whose sum exceeds MAX/NT?
14.3.2 Suppose that the expression
(X + Y*Z) DIV W
ing INTEGER variables X, Y, Z, W occurs in a program, and the programmer wishes to be
ertain that no overflow ever occurs when it is calculated. Given that the machine being used has a
alue of MAX/NT held in the INTEGER variable MI, write appropriate IF statements to protect
-his expression.
4.3.3 In Section 14.3.2 a program Readlnts2 was written to read a series of integers from the
· put, being careful about line markers. That program cannot handle the situation in which the final
!nteger is followed by extraneous characters.
a) Explain exactly what Readints2 does when it fails.
b) Write Readlnts3 that does not fail no matter what characters follow the final integer.
c) Write Readlnts4 to permit arbitrary extraneous characters to follow any of the integers in
the file . (Warning-the most difficult part of this exercise is in stating the requirements for
Readlnts4. Spend as much time as you can thinking about what the program is supposed to
do before starting a. design.)
4.3.4 Which (if any) of the following expressions are legal? Justify your answers by drawing syntax
-rees.
a) - -X
b)-( - X)
d) X * -Y
14.3.5 Given execution state {A·5, B·25, C·IO}, work out the meanings of the following expressions:
B * C MOD (A + 3)
B * C MOD A + 3
B + C DIY A + 3
B + C DIY (A + 3)
B MOD (A DIY B)
14.3.6 Given an integer N in the input, design and test a program to find the sum of integers up to
N.
14.3.7 Given an integer N in the input, design and test a program to find the sum
1 + 2 + · · · + NN.
1 2
14.3.8 Design and test a procedure to print the value of an INTEGER variable, with no preceding
blanks. (Hint: compute and use a format size to print the integer.)
14.3.9 Given an integer N in the input, design and test a program to find the sum of all the factors
(except 1) of N.
14.3.10 Given an integer N in the input, design and test a program to find the maximum difference
between a number and the sum of its factors (except I) for all numbers between 2 and N. Can you
find an analytic solution to this problem?
14.3.11 Given a file of integers, determine the minimum, maximum, and average (including
fractional part to two decimal places).
14.3.12 Given a file of integers, determine the median.
14.3.13 Design and test a module for doing extra-precision addition and multiplication for numbers
stored in representations of your choice on a file.
Preview
When a variable should assume values in a limited range, it may be restricted to a subrange of
another ordinal type. The restriction helps to catch mistakes that would send the values out of
range.
New Pascal types can also be formed by specifying their values to lie within a subrange of t _
values o( an existing ordinal type. The subrange is defined by listing the minimum and maxim u~
constants that are part of the subrange.
<subrange type> ::=<constant> .. <constant>
<constant> ::=<sign> <unsigned number> I <unsigned number>
I <constant identifier> I <character string>
Consider the following declarations:
Smallint = 0 100;
Smallerint = 1 .. 10:
VAR
L: Letter;
X: - Smallint;
Y: Smallerint;
The type from which the constants defining the subrange type are drawn is called the host type. All
operations of the host type may be applied to operands of the subrange type. In the example above,
he host type of Letter is CHAR, and that of Smallint and Smallerint is INTEGER. Thus
CHAR operations can be applied to L, and INTEGER operations to X or Y.
BEGIN
END
Once an enumerated type has been declared, its constants may be used to define subrange
7 pes whose host type is the enumerated type. The declaration of new subrange types (like
· eekDay and WeekEndDay in the following example) are not duplicate declarations of the
"dentifiers used to define the subrange type.
TYPE
DayOfWeek = (Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday, Sunday);
WeekDay= Monday .. Friday;
WeekEndDay =Saturday .. Sunday;
(Since character strings are constants, the syntax rules seem to permit defining subranges of
haracter strings; however, this is not legal.)
14.4.3 Exercises
14.4.1 Which of the following are correct subrange types:
a)Letter = 0 .. 100
b)Reverse = 'Z' 'A'
c)Temp = 32 ... 212
d) One = 1 .. 1
14.4.2 The use of subrange types in assignment statements slows down a Pascal program whe
checks are made.
a) Explain why the speed suffers only when the subrange variable appears on the left side of t
assignment.
b) Make timing comparisons on your computer to determine how bad the slowdown is.
14.4.3 Suppose that for a particular computer, MAX/NT is 1,000,000. Explain why a programm
might want to declare:
VAR
X: -1000000 .. 1000000
instead of
VAR
X: INTEGER
Preview
The design rules for OF Pascal extend to ordinal types with little change. Analysis of programs
is also similar, but the operators of the INTEGER type require some new techniques. The
accumulating iteration is particularly easy to analyze.
X y
X := X + y inrange(X + Y) X+Y
y .- X - y inrange(X + Y- Y) X+Y-Y=X
X := X - y inrange(X + Y- X) X+Y-X=Y
The conditions evidently all hold if the first one does, and this also forces inrange(X) and inrange(Y),
so the function computed by this exchange code is actually:
(z'nrange (X+Y) -+ X, Y : = Y, X)
Overflow is not often treated this formally, but the problem always exists.
As another example of design using the INTEGER type, consider simulating addition by
repeatedly arlding one. That is, to add the values of X and Y, design a WHILE statement to add
one to X, Y ~imes. Overflow will henceforth be ignored, so the desired function is:
Case 1-1
Part Condition X y
IF Y>O X + 1 Y - 1
F Y-1>=0 X+1+Y-1 = X+Y 0
Case 2-1
Part Condition I X I Y
IF Y<=O
F Y>=O I X+Y I 0
Case 2-2
Part I Condition I I X Y
IF Y<=O
F Y<O
14.5.3 Exercises
14.5.1 Given the problem of designing a BEGIN statement to exchange two integer values witho··-
using a temporary variable, i.e., to achieve
X,Y := Y,X
the first step
BEGIN
X := X - Y;
END
satisfies the preservation rule . Complete the design of this BEGIN statement and determine t il?
actual condition for its validity because INTEGER values are restricted to the range [-MAXINT
MAXINT].
14.5.2 Use the design rule for BEGIN statements to achieve
X,Y,Z := X-Y,x+Y,X
14.5.3 Show that the function :
y
(Y>O -+ X, Y : = X - :E Y - i, 0 ) (Y<o -+
i =1
is computed by the WHILE statement:
WHILE Y > 0
DO
BEGIN
Y := -Y - 1;
X := X - Y
END
14.5.4 Show that the function:
(X>=O -+ X := X MOD 3)
is computed by the WHILE statement:
WHILE (X>2) OR (X<O)
DO
X := X - 3
0 if X= 0
1 if X= 1
e) f(X) = 2 if X is prime
3 if X is not prime and X >2
(X,X+1) if X> 100
f) /(X, Y) = { (X, Y) if X< 100
4.6 Summary
t his chapter, Pascal's ordinal data types have been examined. The operations that can be applied
values of each type are summarized below.
Each ordinal type has its use. BOOLEAN variables can be used to record complex conditions
for later testing. INTEGER variables make counting easy, so long as the restriction to [-MAXINT,
MAXINT] is observed. Enumerated types are good fo r capturing a small set of values, each with a
mnemonic name. Subrange types allow the programmer to declare value bounds, that are then
checked automatically.
The analysis methods used for CF Pascal extend to the the ordinal data types almost without
change.
Chapter Preview
D Pascal programs may include sections defining constants and types for use within the
program. They may also define and use functions in much the same way as procedures. They
can handle files that exist outside the program, perhaps created by some other means, or to be
passed on for another use. These features are a convenience, but they also contribute to good
programming style by documenting and localizing program meaning.
Preview
Files whose existence goes beyond the execution time of programs can be used to create program
systems, and to communicate between Pascal programs and other parts of a computer system.
Files may have lifetimes (or extents) like those of other Pascal values (limited to the duration of
procedure or program execution), but they may also continue to exist outside program execution. If
a file variable appears in a program heading (with INPUT and OUTPUT), the file is called external.
The contents of an external file may be read or written by a Pascal program, but the file may exist
before execution begins, and it remains after program execution terminates. For example, in the
END.
the contents of ExtF i le may exist before F i leProcess executes, and remains after execution ,
but the contents of IntFile must be created within FileProcess, and will be destroyed at the
end of execution. Each Pascal machine has different ways of associating Pascal external files with
names in its file system. Often the Pascal identifier is the name used, but may be subject to
restrictions outside Pascal.
External files can have three different uses: input (supplying data to programs from outside),
output (holding data supplied by this program for other programs), and update (bringing data to the
program to be updated for later use). In illustration, an external file could be used to maintain a
cumulative record of students preregistered in a course. Suppose the standard input file INPUT
contains the names of all students who registered today, and Students contains the names of
previously registered students in sorted order. The following design part shows the processing
required.
PROGRAM Register(INPUT, OUTPUT, Students);
{add names in INPUT to those in Students and
report any duplicate names in OUTPUT}
VAR
Students, Templ, Temp2: TEXT;
BEGIN {Register}
{copy INPUT to Templ};
{sort names in Templ};
{merge Templ and Students into Temp2--
if a name occurs in both, send to OUTPUT
and don't duplicate in Temp2};
{copy Temp2 to Students}
END. {Register}
Templ and Temp2 are internal files that disappear at the end of execution. Students has been
updated and will be used in the same program tomorrow (with tomorrow's INPUT).
With external files D Pascal provides the capability of creating program systems, not simply
single programs, in which programs exchange information through external files. For example, in the
illustration above, another program might print the file Students, say as in the design part:
PROGRAM PrintRegistered(INPUT, OUTPUT, Students);
{print the names in Students to OUTPUT}
VAR
Students: TEXT;
BEGIN {PrintRegistered}
{copy and format names from Student to OUTPUT}
END. {PrintRegistered}
Program systems require careful design just as do programs, but now the data structures are the
external files, and the elementary operations are the actions of the programs within the system. In
data processing applications, the person who designs program systems, but not necessarily the
programs within the system, is called a system analyst.
5.2 Constants
Preview
By naming constant values, the programmer can change them throughout a program without
having to search for each occurrence.
nstant values can be given names using declarations like the following:
CONST
LastCh = '#' ;
FieldWidth = 4;
Max = 9999;
Min = -Max;
Name= 'Blaise Pascal';
- ce a constant identifier introduced by a constant declaration names a value, it has the same type
.- t he value and can appear anywhere the value could. In the following example based on the above
ST declarations, Min and Max are used to declare a subrange type; LastCh is compared to a
a racter value; F ieldWidth controls the size of the field in which the Smallint values are
-~in ted; and Name is used in a WRITE statement.
TYPE
Smallint =Min .. Max;
VAR
Value: Smallint;
Ch: CHAR;
BEGIN
READ(Value);
WRITELN(Value:FieldWidth);
WRITELN(Name);
15.2.1 Exercises
15.2.1 Which of the following are legal constant declarations in Pascal?
CONST
CharsPerWord : 4;
StringLength = 15;
WordsPerString = (StrLength+CharPerWord-1) DIV CharPerWord;
Lowint = -MAXINT;
Hello= 'Hello';
15.2.2 Explain why it is reasonable to forbid passing a constant identifier as an actual VAR
parameter to a procedure.
15.2.3 Give an example in which the same constant identifier is used in both the <variable
declaration part> and the <BEGIN statement> of a program so as to prevent a pote ntia
inconsistency between declaration and usage.
Preview
Because types are an important feature of Pascal, it is advantageous to be able to name them,
and use the name instead of the full type description when needed. By defining a complex type
only once, the programmer can prevent accidental transcription errors and make it easy to alter
very occurrence of that type.
·hough data types describe sets of values and the operations that can be applied to the values,
- - al type declarations only describe the representation of the values. New types are declared in
"' <type definition part> section of a block by associating an identifier (the name of the new type)
.-h a description of its values (e.g., listing the constants of an enumerated type or the bounds of a
r ange type). Some examples of new types appear below.
TYPE
CType = CHAR;
Ct = CType;
Month = (NoMonth, Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec);
Summer = Jun . . Aug;
DayNum = 1 .. 31;
Letter= 'A' .. 'Z';
identifiers like Letter can occur anywhere primitive types occur in declarations and formal
m eter lists. Type identifiers are a convenient shorthand- by naming a list of constant
- ifiers like Month it can be easily repeated in a program without the danger that it might be
:ed incorrectly.
The syntax of type declarations appears below:
<type definition part> ::=TYPE <type definitions> l
<type definitions> ::= <type definitions> <type definition>
: <type definition>
<type definition> ::= <identifier> = <type denoter >
<type denoter> ::=<type identifier> l <new type>
<new type> ::= <new ordinal type>
<new ordinal type> ::= <enumerated type> l <subrange type>
Two types, T1 and T2, are compatible if either of the following is true:
T1 and T2 are the same type.
T1 is a subrange of T2, or T2 is a subrange of T1, or both T1 and T2 are subranges of the
same host type.
Thus Digit can be compared to the constant '0' (which has type CHAR) and to Letter.
A value of type T2 is assignment-compatible with a type T1 if either of the following is true:
T1 and T2 are the same type (but not a file type).
T1 and T2 are compatible ordinal types and the value of type T2 is in the closed interval
specified by the values of type Tl.
In the following example, only the first two assignment statements are legal.
TYPE
Smallint = 0 .. 99;
Smallerint = 0 .. 9;
VAR
X: Smallint;
Y: Smallerint;
BEGIN
X := 0; {The type of X (Smallint) is compatible with
the type of 0 (INTEGER) and 0 is in
Smallint's closed interval}
Y := X; {The type of Y (Smallerint) is compatible
with the type of X (Smallint) and the value
of X (0) is in Smallerint's
closed interval}
Y := X-1 {The type of Y (Smallerint) is compatible
with the type of X-1 (INTEGER) but the
value of X-1 (-1) is outside Smallerint's
closed interval}
END
15.3.1 Exercises
15.3.1 In the following Pascal fragment, identify the errors (if any).
TYPE
Smallint = 0 .. 99;
Smallerint = 0 .. 9;
VAR
W: 0 •• 9;
X: Smallint;
Y: Smallerint;
Z: 0 .. 9;
Letter : 'A' 1 z I;
z : = 0;
X := 0;
W := Z;
IF (Digit <= 1 0 1 ) OR (Digit =Letter)
THEN
WRITELN( 1 hello 1 ) ;
y :=: X;
P(X,Z);
P (X, X-1) ·;
P (W, Y)
END
15.3.2 The technical rules about Pascal type agreement for variable usage range from obviously
necessary to rather arbitrary. Give an example of a rule that is obviously necessary and explain
why. Give an example of a rule that seems merely arbitrary.
Preview
Parameters passed by value are an isolation mechanism between the ·point of call and the
statements of the called routine. No actions in the routine can transmit information back to
the caller through the value parameter. Thus these parameters serve as a safe way to provide
read-only input values.
There are two kinds of formal parameters in D Pascal: variable parameters and value parameters.
In a procedure declaration variable parameters look like declaration statements. They begin with
the reserved word VAR, followed by a list of identifiers separated by commas that are the names of
the formal parameters, a colon, and an identifier giving the parameter type. Value parameters are
declared like variable parameters except that the leading VAR is omitted. In the following example,
VarParm is a variable formal parameter of type T and ValParm is a value formal parameter of
type T.
PROCEDURE P(VAR VarParm: T; ValParm: T);
The syntax rules describing parameters are:
<formal parameter> ::= <variable parameter> I <value parameter>
<variable parameter>::= VAR <identifier list> : <type identifier>
<value parameter> ::= <identifier list> : <type identifier>
When an actual parameter is bound to a variable formal parameter, an alias is set up between
the name of the actual parameter (which must be a variable rather than a constant or expression)
and the name of . the formal parameter. The word alias is used because when the procedure
statement is replaced by the body of its procedure declaration, variable formal parameters are
replaced by their corresponding actual parameters. Thus, each time a value is assigned to a variable
formal parameter the value of the aliased actual parameter is changed. The type of the actual
parameter must be the same as t he type of the formal parameter.
15.5 Functions
Preview
Like procedures, functions encapsulate extensive computations so that they may be used easily,
and may be modified by transmitting different arguments when used. However, functions return
values, and are called by naming them within expressions.
Procedure declarations encapsulate sequences of operations, often to compute and return a value ·
t hrough a variable parameter. Function declarations and calls in Pascal provide another way of
returning the computed value. Function declarations look much like procedure declarations, but the
header ends with a colon and a type identifier, giving the type of the result . A procedure
communicates its results by assigning values to its parameters. Within the block of a function
declaration, the result of the function is indicated by assigning it to the identifier that is the name of
t he function. If no assignment is made to this identifier in the block, the result of the function
invocation is undefined. The mechanism for returning results is illustrated by Max2 below:
FUNCTION Max2(Pl, P2: INTEGER): INTEGER;
{ (Pl > P2 --> Max2 := Pl) I
(P2 >= Pl --> Max2 := P2)}
BEGIN {Max2}
IF Pl > P2
THEN
Max2 := Pl {return Pl as Max2's value}
ELSE
Max2 . - P2 {return P2 as Max2 ' s value}
END; {Max2}
The syntax rules for function definitions appear below:
<procedure and function declaration part> ::= <proc/func declarations> l
<proc/func declarations> ::= <proc/func declaration>
l <proc/func declarations> <proc/func declaration>
<proc/func declaration> ::=<heading> ; <block> ;
<heading> ::= PROCEDURE <identifier> <formal parameter list>
l FUNCTION <identifier> <formal parameter list> : <result type>
Function calls look like procedure calls: the name of the function is followed by a list of actual
parameters, which are expressions, separated by commas. However, there is an important difference
between function and procedure calls. Since functions deliver values, function calls are expressions
which m ay appe ar within larger expressions or alone wherever an expression could appear in a
program. Procedure calls, wh ich do not deliver values, are st atements. To find the maximum of
t hree values, Ma x3 could be declared as follows, using two calls to Max2 as right sides of
assignment statements.
Where F' is a new identifier, and T' is the same as T except that every occurrence ofF on the left
side of an assignment in T-is replaced by F' in T'. That is, a temporary st a t e is created by adding
a new identifier F' to s; the modified body of the function acts on the te mporary st ate; then the
There are many cases in which the fetch order does not alter the value of an expression, but
because of the potential surprise that any side effect may cause, it is good programming practice to
avoid side effects in functions. When it is necessary to modify a variable parameter, a procedure is a
better choice, and modification of global variables within functions or procedures should be avoided.
However, SUCC ('A') may not have value B on some Pascal machines, because the values A and
B are not represented by adjacent codes in the underlying machine. Thus these built-in functions
cannot sensibly be used with alphabetic characters on all machines, and the careful programmer
avoids using them even if they work properly on today's_machine.
Since only a subset of the integers can be represented in a Pascal program, the functions SUCC
and PRED are defined for INTEGER expressions in the obvious way:
jsucc( E ) j(s) =IE + 1j(s),
jPRED ( E ) j{s) =IE - lj{s),
where these equations include the possibility that the value of E is ±MAX/NT, and the
corresponding addition or subtraction is undefined.
SUCC and PRED can be applied to enumerated-type operands to produce values in the
sequence defined by the declaration. For example, consider the declaration:
TYPE
Month = (NoMonth, Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec);
The order of appearance of the identifiers in the type declaration is from the constant with the least
value to that with the greatest value. For example:
Ex ression Value
SUCC(Jan) Feb
PRED(Oct) Sept
PRED(NoMonth) undefined
SUCC(Dec) undefined
Enumerated types can be used to eliminate "mystery" numbers from programs. Mystery
numbers are those that arise from arbitrary mappings of real-world values to numerical values. If
the programmer had made up integer values for the months instead of declaring TYPE Month, 0 for
January, 1 for February, etc., a program to list the number of days in each month might be:
Mo := Jan; Mo := Jan;
WHILE Mo <= Dec {statement list};
DO WHILE Mo < Dec
BEGIN DO
{statement list}; BEGIN
Mo := SUCC(Mo) Mo := SUCC(Mo);
END {statement list}
END
The WHILE statement was "unrolled" by repeating its body to handle the case that Mo has the
value Jan, and incrementing Mo before executing the body in the BEGIN statement. Thus, on the
last execution of the BEGIN statement, Mo has the value Dec and the next test of the WHILE
condition fails. (Another way to solve this problem would be to move NoMonth to the end of the
enumeration, providing a phony month after Dec.)
SUCC and PRED are not often used with the BOOLEAN type, but the results can be predicted
from the information that this type acts as if it had been declared:
TYPE
BOOLEAN = (FALSE, TRUE)
Expression Value
ODD(-5) TRUE
ODD (10) FALSE
ABS ( - 5) 5
ABS (12) 12
SQR(-5) 25
SQR (10) 100
Programmers who are more comfortable wi t h the INTEGER type than with enumerated types
often use ORD to excess, and thereby lose the advantages of the mnemonic constants.
15.0.1 Exercises
15.0.0 A function call and a procedure call have the same syntactic form-an identifier followed by
an optional list of actual parameters. Can the two forms be used interchangeably? How is your
answer reflect ed in the syntax rules?
15.0.0 Suppose that Max2 (Section 15.5) had been written with variable parameters instead of
value parameters. Explain why such a Max2 could not be used as an argument to itself in the
second version of Max3.
15.0.0 Consider a Pascal FUNCTION that does digit input. Its header is
FUNCTION DigitR: INTEGER;
and each time it is called, the next character is read from INPUT, converted from CHAR to
INTEGER, and returned . If the next character is not a digit , -1 is returned. For example , if INPUT
contains the string f123Xt then the value of
DigitR + DigitR + DigitR + DigitR
is 5.
a) Write Digi tR using ORD .
b) Write Digi tR without using ORD.
c) Use Digi tR to write another function that reads strings for base-ten numbers in a fixed
range, returning the number if in range, or -1 if out of range:
FUNCTION BaselO(MaxVal: INTEGER): INTEGER;
{Interpret a string of digits followed by a nondigit
as a representation of a base-ten integer N.
If 0 <= N <= MaxVal, return the value; otherwise,
return -1.}
d) Are there side effects in Digi tR? In BaselO? How could side effects cause a programmer
trouble in this case?
15.5.4 Each Pascal fragment below contains the identifier Qu. You are to deduce a possible type of
Qu from the context in each separate case. Express the answer as a type specification for the
declaration:
VAR
Qu: ?
Replace the question mark with the type required of Qu. For example, if you decide that one answer
is a subrange of CHAR between 3 and 6, fill in:
VAR
QU: I 3 I • • 16 I
c)CHR(ORD(SUCC(Qu)))
15.5.5 What values are printed by the following programs?
a)PROGRAM Alias1(0UTPUT);
VAR
X: INTEGER;
PROCEDURE P(VAR A,B: INTEGER; C: INTEGER);
BEGIN {P}
A := A + 1;
B : = B * A;
C : = C + X;
WRITELN (A, B, C)
END; {P}
BEGIN {Alias1}
·X := 1;
P(X,X , X);
WRITELN(X)
END. {Alias1}
b)PROGRAM Alias2{0UTPUT);
VAR
X, Y: INTEGER;
PROCEDURE P(VAR A,B: INTEGER; C : INTEGER);
BEGIN {P}
A := A + 1;
B := B * C;
C := X * Y;
WRITELN(A,B,C)
END; {P}
BEGIN {Alias2}
X := 1; Y : = 2;
P(X,Y,X);
WRITELN(X,Y)
END. {Alias2}
15.5.6 Write a program to print out the absolute sequence positions of the characters
0 ... 9a .. .zA.. . Z in your Pascal m ac hine. Explain why it is or is not all right to use SUCC to pass from
characte r to character in the progra m .
15.5.7 Explain why the formal definition of function-call meaning in Section 15.5 is not the proper
in tuitive meaning when there are side effects. Give an example of a call where the meaning is wrong,
showing both what the definition provides, and what it should provide.
Preview
Pascal's rule that identifiers must be declared before use is saved in the case of mutual recursion
by the FORWARD declaration.
With mutually recursive procedures (e.g., two procedures each calling the other) Pascal's rule to
declare identifierS' before using them cannot be observed. For example, the call of Second precedes
he declaration of Second in the following example, but moving Second before First will only
result in the call of First preceding its declaration.
PROCEDURE First(IVar: INTEGER);
BEGIN {First}
... ,
Second(CHR(IVar));
END; {First}
PROCEDURE Second(CVar: CHAR);
BEGIN {Second}
... ,
First(ORD(CVar));
END {Second}
Pascal provides a way to preserve its rule of declaration before use in the FORWARD declaration.
One of the mutually recursive procedures has its heading moved up, but its body remains in place. A
FORWARD declaration includes the entire procedure h_eading (name, formal parameter list, return
ty pe), but replaces the block with the reserved word FORWARD. The procedure's block appears later
in the program, but only the reserved word PROCEDURE or FUNCTION and the name of the
procedure may appear with the block. The parameters and return type are not repeated. For the
example above:
Second(CHR(IVar));
END; {First}
PROCEDURE Second; {don't repeat parameters}
BEGIN {Second}
• • • I
First(ORD(CVar));
END {Second}
15.7 Summary
Several declarations were introduced in this chapter: external files, constants, types, value
parameters, and functions. External files provide a way to create program systems. Constant and
type declarations help organize programs by cen,tralizing information, making a program easier to
change. For example, consider the following:
TYPE
T = ... ; {any type with> and:= operations}
FUNCTION Max2(VAR Pl, P2: T): T;
{ (Pl > P2 --> Max2 := Pl) I
(P2 >= Pl --> Max2 := P2)}
BEGIN {Max2}
IF Pl > P2
THEN
Max2 := Pl
ELSE
Max2 := P2
END; {Max2}
By simply changing the definition of T (say from CHAR to INTEGER), Max2 could be applied to
integers rather than characters.
Pascal procedures may have value parameters. Value parameters are more secure than
variable parameters because inadvertent assignments to them do not change the execution state
where the call was made . Thus, input values are passed as value parameters, and output variables
or variables to be updated are passed as variable parameters.
Like procedures, functions help structure programs by abstracting behavior . However, function
calls are expressions that re turn values, while procedure calls are statements that act by changing
either their variable formal parameters or some nonlocal variables. Also, since functions can only
return a single value, procedures must be used to return multiple values through assignments to
variable parameters.
Chapter Preview
This chapter describes three mechanisms for creating composite values in Pascal: sets, files and
records. A limited version of set objects is provided. Files extend the TEXT files of CF Pascal
t o types other than CHAR. Records providing a grouping mechanism for any finite collection of
t ypes.
Each ordinal type contains values that are atomic- the values are not composite. Aggregate
types, on the other hand, combine previously defined types, which may be ordinal or other aggregate
.ypes, into new types that contain components of the previously defined types.
<new type> ::= <new ordinal type> I <aggregate type>
<aggregate type> ::= <unpacked structured type>
<unpacked structured type> ::= SET OF <base type>
I FILE OF <component type>
I RECORD <field list> END
<base type> ::=<ordinal type>
<ordinal type> ::= <new ordinal type> I <ordinal type identifier>
<ordinal type identifier> ::= <type identifier>
<component type> ::= <type denoter >
" ets, files, and records combine component types in different ways. Set and file types group identical
components, while record types group components that differ. The set and record types group
components without regard to order, while file types form sequences of components. Any component
of a record may be selected; the components of a file may only be accessed sequentially; and no
selection operation at all is defined for set components.
16.1 Sets
Preview
Pascal's set data type allows the programmer to declare and use variables that are sets whose
members are drawn from (small) finite collections. A full range of set operations is available.
{{}, {1}, {2}, {3}, {1, 2}, {1, 3}, {2, 3}, {1, 2, 3}},
so any of these sets may be values of type Small IntSet. The Pascal set constant for the value set
that has three elements would be written [1, 2, 3] or [1 .. 3].
Any of the following assignment statements initialize Sieve to the set value {2,3, ... 19,20}:
Sieve : = [2 .. 20];
Sieve : = [2,3,4,5,6,7,8,9,10,11,12,13,
14,15,16,17,18,19,20];
Sieve:= [2 .. 9,10,11 .. 15,16,17,18 .. 20]
The operations on set values are + (set union), - (set difference), * (set intersection); =
(equality), <> (inequality), <= (nonstrict subset), >= (nonstrict superset); and IN (set
membership).
The union, difference, and intersection operations are defined only for pairs of sets, not set
components. Thus if Sieve has integer members it is illegal to write:
Sieve + 2
Instead, the element must be made into a 1-set value:
Sieve + [2]
Some sample relational expressions and their values are:
Expression Value
[3, 5] <= [3 . . 5] TRUE
[3 5] <= [3 .. 6] TRUE
[3 5] >= [3 .. 5] TRUE
[3 5] >= [3 .. 6] FALSE
:fie square brackets are not only used for set constants, but to form sets from expressions whose
alues are the member elements. For example, if X is of type INTEGER with value 3:
Expression Value
[2 X]
1 {2, 3}
[3 .• X] {3}
[X .. X+2 1 SUCC(X+7)] {3, 4, 5, 11}
[X .. 1] {}
-::>roperties of the set operators are shown below:
Ev aluations of some sample set values are shown below in the Pascal constants notation.
[1] + [7 o o 9] * [7 1 9] = [1] + [71 9] = [11 71 9]
[4 .. 6] - [41 6] + [1] = [5] + [1] = [51 1]
END. {AlphaCount}
The sieve is now empty and the primes up to 16 are 2, 3, 5, 7, 11, 13.
The set type is exactly the right data type to implement this algorithm. In the design of a
solution for the prime number problem, the sieve is represented as a set of integers in order to use
t he set difference operator to remove elements from the set.
Design part 1
PROGRAM Prime(OUTPUT);
{Print the prime numbers between 2 and Max}
CONST
Max = 16;
VAR
Sieve: SET OF 2 .. Max;
Next, NextMult: INTEGER;
BEGIN {Prime}
Sieve := [2 Max];
Next := 2;
WHILE S i e v e < > []
DO
BEGIN
{print smallest member Next of Sieve};
{remove multiples NextMult of Next from Sieve}
16.1.4 Exercises
16.1.1 Explain why sets would not be very useful on a Pascal machine that limited the underlying
-ype to have less than 26 elements.
16 .1.2 How many set values of the type SET of BOOLEAN are there?· How many members has
he set of this type with the most members?
16 .1.3 Suppose that for the declaration
VAR
SetSized: SET of Typ
-he underlying type Typ has exactly five possible values.
a) How many possible different values are there for SetSized?
b) Explain why no value of SetSized can have more than five members.
6.1.4 Rewrite the program Pr intDays of Section 15.5.2 so that it tests for the 30-day months
sing a constant of type SET of Month .
16.1.5 In the context of the following program fragment:
VAR
A: SET OF 1 . • 20;
B,C,D: INTEGER;
BEGIN
A : = [1. . 20];
B : = 5·'
c : = 8·'
D : = 10;
ev aluate the following expressions:
a) NOT ( [B, C, D] <= A)
b) [B .. C] + [D] * [2 .. 8]
c) [B .. C] - A
d) B IN A
,. .
a) 'A' IN Qu
b) Qu + [3]
16.1.7 Show that the following program fragment:
S := []; I := 0; N := 4;
WHILE I <= N
DO
BEGIN
I := I + 1;
S : =
S + [I]
END
computes the function:
_ S,I,N := [1. .5] ,5,4
You may assume that the function of the WHILE statement is:
(I<=N ~ S, I : = {I+k I 1 <k<N-I+1}, N+1)
(I>N ~ )
16.2 Files
Preview
Files are ordered sequences of values. WRITE statements append a value to an existing
sequence (always at the end), and READ statements can obtain the values in the same order
they were earlier written. The values are not limited to CHAR as in TEXT files, but may be of
almost any Pascal type. Buffer variables and PUT /GET statements provide an additional way
to manipulate files.
In addition to character files, Pascal lets us define new file types with components of any previously
defined type other than file or another aggregate type containing a file as a component. A typical
type declarationfor a file is:
VAR
IntFile: FILE OF INTEGER;
An IntF i le value is a sequence of integer values-only INTEGER quantities may be read from, or
written to, the file. File variables are the only Pascal variables that cannot be the target of
assignment statements. All file values must be created using predefined operations.
A file declaration implicitly declares a buffer variable written as the file identifier followed by
an up-arrow (e.g., IntF i let) with the same type as the components of the file. When we are
reading a file, IntFilet contains the value currently available for reading. New file components
are appended to a file by filling the buffer variable and writing its contents. Buffer variables can
appear anywhere other variables can appear, e.g., in expressions:
3 + IntFilet * 7
However, buffer variables are not identifiers since they end in a character which is neither a letter
nor a digit. Thus, we must update our syntax rules, replacing <identifier> on the right side of the
RESET REWRITE
REWRITE
RESET
END
Then values are assigned to IntListt so that subsequent PUT statements will append the values
to IntList:
{equivalent of WRITE(IntList, 1)}
IntListt := 1;
PUT(IntList); {appends 1 to IntList, IntListf undefined}
{equivalent of WRITE(IntList, 2)}
IntListf : = 2;
PUT(IntList) {appends 2 to IntList, IntListf undefined}
At this point, IntList contains the values 1 and 2 in its past list, the file is writable, and the
contents of IntListf are not defined.
lntList = < <1,2>,<>,W > and IntListt =undefined
RESET and GET are the operations that retrieve values from files. RESET returns its file to
its starting position so that reading can begin. The file's buffer variable is assigned the value of the
first component of the file's future list {if one exists).
!RESET (F) I= {( u, v): F is not INPUT or OUTPUT and v = u except that
v{F) = <<>, u{F).1 & u(F).2, R> and v(Ft) = e(u(F).1 & u(F).2}.
Since the head function 9 is undefined for empty lists, the buffer variable is undefined when RESET
is applied to empty files.
If a file is open for reading and its future list is not empty, GET transfers the first element
from the future list to the past list, and assigns the value of the new first element of the future list
to the file's buffer variable.
lGET (F) I= {(u,v): u{F) .3 = Rand u(F) .2 =I=<> and v = u except that
v(F).1 = u(F).1 \7(8( u(F).2)), v(F).2 = A{ u(F).2), and
v(Ft) = e(A{ u(F).2))}.
If a file is not open for reading, the statement aborts and program execution will halt. The statement
will also abort if there is no data left to read (i.e ., the future list is empty).
The file IntList constructed above has two integer values in its past list and is open for
writing; that is, it's value is <<1,2>, <>, W > . A RESET operation changes the values of both
IntList and IntListt to <<>, <1,2>, R> and 1, respectively . Now a GET on IntList
moves 1 to the past list of the file so the value of IntList becomes:
<V(e(<1,2>)), A{<1,2>), R> = <<1>, <2>, R>,
and the buffer pointer IntListt has value
e(A{<I,2>)) = 8(<2>) = 2.
The end-of-file function EOF returns a Boolean value: TRUE if no more components remain in
the file's future list and FALSE otherwise.
jEOF (F) l(u) = (u(F).2 = <>).
If EOF is true, the value in the file buffer may not be accessed beca use it i::: undefi ned.
'
EOF (IntList) is FALSE after the first GET described above, since IntList has a future list
containing 2. A second GET makes the file value
<<1> V'(e(<2>)), A(<2>), R> = <<1,2>, <>, R>
and the buffer-variable value
e(A(<2>)) = 9(<>)
which is undefined . The value of EOF (IntList) is now TRUE. To avoid an attempt to access a
b uffer variable when it is undefined we adopt programming paradigms in which EOF is always tested
before the buffer is accessed. For example:
RESET(F);
WHILE NOT(EOF(F)) DO
BEGIN
Fl ... {access the buffer variable}
GET (F)
END
Varn := FileVart;
GET{FileVar)
END
Similarly,
WRITE{FileVar, Expl, ... , Expn)
where Expl, ... , Expn are expressions of the appropriate type, has the effect of:
BEGIN
FileVarf := Expl;
PUT{FileVar);
FileVarf := Expn;
PUT{FileVar)
END
If a Pascal machine supports READ and WRITE on non-TEXT files, OddBe foreEven can be
rewritten without using buffer variables:
16.2.51nteractive Input/Output
To this point, all the programs executed by the Pascal machine have been batch-processed, i.e., the
programs and all the data are presented to the machine at the same time, the program is executed,
and the results are printed. Many programs are designed to be run interactively, prompting the user
to enter more data after execution has begun and displaying some results before all the data has
been entered. MinMax below is a typical interactive program. It reads a single character
command that guides the program to print either the higher (requested by the command H) or lowe r
(requested by L) of the two characters following the command. The program continues comparing
characters as long as the first character on an input line is a valid command. When an invalid
command is encountered, the program terminates.
PROGRAM MinMax(INPUT,OUTPUT);
VAR
Command,Chl, Ch2: CHAR;
PROCEDURE SortTwo(VAR Chl, Ch2: CHAR);
{ <Chl ,Ch2> := <min(Chl,Ch2),max(Chl,Ch2)>}
VAR
Temp: CHAR;
BEGIN {SortTwo}
IF Chl > Ch2
THEN
BEGIN
Temp := Chl;
Chl := Ch2;
Ch2 := Temp
END
END; {SortTwo}
BEGIN {MinMax}
WRITELN( 1 Enter <H or L> <chl> <ch2> 1 ) ;
READ(Command);
WHILE (Command = 1 H 1 ) OR (Command = 1 L 1 )
DO
BEGIN
READLN(Chl,Ch2);
SortTwo(Chl,Ch2);
IF Command = 1 H 1
THEN
WRITELN( 1 The greater character is I I I
Ch2, I I I I )
ELSE
WRITELN( 1 The lesser character is I I I
Chl, I I I I ) ;
READ(Command)
END
END. {MinMax}
Execution
OUTPUT:Enter <H or L> <ch1> <ch2>
INPUT :HFG
OUTPUT:The greater character is 'G'
INPUT :LFG
OUTPUT:The lesser character is ' F'
INPUT :J
OUTPUT:
As is typical of interactive program, MinMax prompts its user for input with the line:
Enter <H or L> <chl> <ch2>
a nd only a user unacquainted with BNF would think that the angle brackets should be entered in
r esponse. To be useful this line must appear immediately , but there is a peculiarity of Pascal that
m ay hold it until some input has been entered. Program execution begins with an implicit
RESET (INPUT) ;REWRITE (OUTPUT). The RESET does a GET (INPUT) to place the first
charac te r of t he fi le in I NPUTt, bu t no data has been entered because the user is waiting for the
prom p t! F ur thermore, when no d ata has bee n entered what is the value of EOF or EOLN? Pascal
machines usually get around these problems by defining default values for INPUTf, EOF, and
EOLN, or using lazy evaluation strategies. The programs in this text assume lazy evaluation.
With these values, a program's first READ statement obtains a blank, and INPUTf assumes the
value of the first character actually in the file. Thus when a Pascal machine adopts this method, the
programmer must do an initial GET or READLN to discard the first character.
Lazy evaluation is a strategy by which values are not computed until they are needed. Since
no value associated with INPUT is needed in MinMax until the first READ statement
(READ (Command)) is encountered, execution of the implicit RESET (INPUT) is postponed until
after the first WRITELN statement.
In either case, an initial WRITE statement can provide a prompt for an interactive program .
However, if lazy evaluation is used, no extra READ statement is required, .and using one will discard
the first real character of input. So experimentation with the Pascal machine is required before
interactive programs can be written.
16.2.6 Exercises
16.2.1 TEXT files may be examined using either READ statements or through the buffer variable .
Give a typical program loop that uses a READ statement to obtain each element in a TEXT file ,
ignoring the line structure . Then write an equivalent program using the buffer-variable method of
access.
16.2.2 The OddBeforeEven program makes two passes over the file Intin. Redo the entire
design so that only one pass is required, using a temporary file if required. Instead of using Out ,
send the results directly to OUTPUT.
16.2.3 Design and test a programming system using two external files, one a FILE OF Month and
the other a FILE OF INTEGER. One program in the system writes these files, putting in the
month and number of inches of rainfall for that month . (Simply assume that each month a separate
program is made up to write that month's data, and give the program for a February where the
rainfall was 3 inches.) The second program summarizes a year's data and prints out a table; this
program is to be run after the December data is entered. Is a third program needed to initialize the
files?
16.2.4 The following declarations are in force for this question:
TYPE
Drink= (Coffee, Tea, Milk);
VAR.
Ch: CHAR;
Itg: 3 .. 33;
Enm: Drink;
EFile: FILE OF Drink;
FUNCTION Ff(VAR. Prl: CHAR; Pr2: CHAR): BOOLEAN;
In each part below there is at least one mistake. Locate and explain each one.
a)IF Ff(Ch, Ch) = 1
THEN
EFilef := SUCC(Tea)
b) (within the body of Ff):
F f : = Ch IN [ 1 A 1 , Enm]
. ~
c)IF (Coffee< Tea) AND (Ff('A', 'B'))
THEN
Itg : = Itg + 32
d)IF [3, 44 , Itg] +[Tea, PRED(Tea)] []
THEN
RESET (EFile)
16.2.5 Extend the text-justifying problem of Section 16.2.4 to include rearranging the words on lines
so that no line is too long. That is, the words are to be taken from INPUT, and justified lines are to
be sent to OUTPUT, with enough words to fill, but not overfill each line. The most straightforward
a lgorithm is to read words until the sum of their lengths (including one-blank separators) just
exceeds the output length available, then justify the line using all but the final word, which is taken
to start the next line. Give a program design that solves the extended problem, using as many of the
routines from Section 16.2.4 as possible.
16.2.6 The simple algorithm suggested in the previous exercise does not always produce the best
looking text.
a) Describe how simple hyphenation could be added to the algorithm, where only the following
suffixes are broken off by hyphen:
-tion
-ing
-ed
b) Describe how the algorithm could be modified to hold three lines, and minimize the number of
spaces that are inserted by moving words between lines. Give an example in which the three-
line algorithm would produce text with a better appearance.
16.2.7 Investigate the use of prompt lines in interactive programs on your Pascal machine.
a) Is an initial discarded READ statement needed?
b) Explain how a program expecting free-form input in which all leading blanks are of no
significance, can be written with a prompt line so that it works on Pascal machines, whether
they use a default value for the buffer variable or lazy evaluation.
16.3 Records
Preview
When a complex data object is to be used in a program, it is convenient to have a single name
for it, and to declare it without explicitly repeating all the names of its parts. The Pascal
i"ecord types allow the object's description to be given once, then used either by its composite
name, or by naming its separate parts.
Records permit a fixed number of related data items of (possibly) different types to be grouped
together in a single object . Each data item, called a field, consists of a name, a colon, and a type.
In the following example, there are two record types, Date and Workerinfo, each containing
t hree fields. Some of the fields of the Workerinfo type are themselves records. Thus, record types
can be nested to create descriptions of arbitrarily complex objects.
. t
'
'
"
TYPE
Month =
(NoMonth, Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec);
DayNum = 1 .. 31;
ValidYear =
1850 .. 2001;
Date = RECORD
Mo: Month;
Day: DayNum;
Year: ValidYear
END;
Workerlnfo =
RECORD
SSNumber: INTEGER;
BirthDay: Date;
FirstHired: Date
END;
When a variable has been declared to be of a record type, its name refers to the entire record .
As a summary name, the variable is useful, for example when the entire record is to be passed as an
argument to a procedure. However, to manipulate the parts of a record, other names are needed.
The fields are named by joining their identifiers (from the type declar ation) to the variable name
with a period . For example, the variable
VAR
Worker: Workerlnfo;
declares Worker to be a record with the three fields named in the type declaration above as
SSNumber, BirthDay, and FirstHired. The names of the parts of this particular
Workerlnfo are
Worker.SSNumber
Worker.BirthDay
Worker.FirstHired
where the first is an elementary item, and could be given a value:
Worker.SSNumber := 123456789
The other two parts of Worker are themselves records, and their parts must be further identified.
For example:
Worker.BirthDay.Mo := Feb;
Worker.FirstHired.Day := 22;
The values of record variables encompass all the possible values of their components, in a ll
possible combinations. Some of these combinations may not correspond to the idea that the
programmer had in mind for the type . For example, the author of the type Date above has made
an attempt to restrict each field to sensible values, but nothing prevents the construction of dates
like February 29, 1961, or September 31, 1901 in which there is a mismatch between the components.
Within a record, field names must be unique . However, since references to fields must contain
the names of all records of which the field is a component, fields in inner records can be named by
the same identifiers as fields in outer records or other variables. No ambiguity arises from this reuse
of identifiers. Thus the following Pascal fragment is legal:
""'
TYPE
Date = RECORD
Mo: Month;
Day: DayNum
END;
{Include PROCEDURE ReadMonth(VAR Fin: TEXT; VAR Mo: Month)}
PROCEDURE ReadDate (VAR Fin: TEXT; VAR Result: Date);
{Fin.3=R and length(Fin.2)>=5 -->
read three characters representing a Month and
an integer representing a day from Fin . 2, and
assign the corresponding Month and DayNum values
to Result.Mo and Result.Day}
BEGIN {ReadDate}
ReadMonth(Fin,Result.Mo);
READ(Fin,Result.Day)
END; {ReadDate}
{Include PROCEDURE WriteMonth(VAR FOut: TEXT; VAR Mo: Month)}
PROCEDURE WriteDate(VAR FOut: TEXT; VAR D: Date);
{FOut.3=W and D.Mo<>NoMonth -->
write the character representation corresponding
to the values of D.Mo and D.Day to FOut.l}
BEGIN {WriteDate}
WriteMonth(FOut,D.Mo);
WRITE(F0ut,D.Day:3)
END; {WriteDate}
FUNCTION Less(VAR Dl, D2: Date): BOOLEAN;
{ Less := Dl < D2 }
BEGIN {Less}
IF Dl.Mo < D2.Mo
THEN
Less := TRUE
ELSE
IF Dl.Mo > D2.Mo
THEN
Less := FALSE
ELSE {Dl.Mo = D2.Mo}
Less := (Dl.Day < D2.Day)
END; {Less}
Although Less is a function that does not modify its parameters, they are declared as variable
parameters rather than value parameters. Value parameters require a copy operation (hidden by
t he Pascal machine) to ensure that changes within the function cannot reach the outside; this
overhead is not present with variable parameters. Some Pascal machines do not permit the use of
r ecord types in value parameters for this reason.
In these data types, the type and procedure declarations have been kept in the same unit of
scope, to emphasize their connection. This method of organization produces a "flat" procedure
structure-all the procedures are declared in the same unit of scope and (avoiding the restriction of
d eclaration before use) any procedure can be called by any other procedure. A different, more nested
program structure could also be adopted based on procedure-call relationships. For example, since
ReadMonth is called only by ReadDate, the declaration for the former could be nested within the
declaration of the latter. A similar relationship holds between Wr i teMonth and Wr i teDate .
Thus, the following program structure could be used:
END; {ReadDate}
END; {WriteDate}
END; {Less}
With this organization, only ReadDate can call ReadMonth; only Wr i teDate can call
Wr i teMonth. Restricting the availability of procedures hides information. Whether the fia t
structure emphasizing the relationship between type definitions and operations is better than th e
nested structure hiding names is a matter of taste.
The program to sort dates can be developed with either of the organizations above, and most
of the work is already accomplished by the existence of the right data types. A single date is read
and inserted in a file of dates (DateFile). Subsequent dates are inserted in the file so that it
remains in ascending order.
Design Part 3
PROGRAM SortDates(INPUT,OUTPUT);
TYPE
Month= (NoMonth, Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec);
DayNum = 1 .. 31;
Date = RECORD
Mo: Month;
Day: DayNum
END;
FileOfDate = FILE OF Date;
VAR
DateFile: FileOfDate;
{Include ReadDate, WriteDate, Less, ReadMonth, WriteMonth}
BEGIN {SortDates}
REWRITE(DateFile);
ReadDate(INPUT,DateFilet);
READLN; {Skip over line marker between dates}
PUT(DateFile);
WHILE NOT EOF
DO
{insert a new date in ascending order
in DateFile};
{copy DateFile to OUTPUT}
END. {SortDates}
To keep the values in DateFile in ascending order, all those dates smaller than a new date Dare
copied from DateFile to a temporary file TFile, D is written to TFile followed by the
.
"
,
•
remaining dates in DateFile, and TFile is copied back into DateFile. (Declarations for D and
TFile must be added to Design Part 3.)
Design Part 3.1
{insert a new date in ascending order in DateFile}
BEGIN
ReadDate(INPUT,D);
READLN; {Skip over line marker between dates}
{copy elements smaller than D
from DateFile to TFile};
TFilet := D;
PUT(TFile);
{copy remainder of DateFile to TFile};
{copy TFile to DateFile}
END
Dates are copied from DateFile to TFile until either no more dates remain in the former or
until a date is greater than the value of D (i.e., Less (DateFilet,D) has the value FALSE). A
declaration for the Boolean variable Copying must be added to Design Part 1.
Design Part 3.1.1
{copy elements smaller than D from DateFile to TFile}
BEGIN
RESET(DateFile);
REWRITE(TFile);
Copying := TRUE;
WHILE NOT EOF(DateFile) AND Copying
DO
IF Less(DateFilet,D)
.THEN
BEGIN
TFilet := DateFilet;
PUT(TFile);
GET (DateFile)
END
ELSE
Copying := FALSE
END
The three copying operations are all straightforward. When DateFile is copied to OUTPUT,
only one date is written on a line.
Design Part 3.1.2
{copy remainder of DateFile to TFile}
WHILE NOT EOF(DateFile)
DO
BEGIN
TFilet := DateFilet;
PUT(TFile);
GET(DateFile)
END;
16.3.3 Exercises
16.3.1 Suppose that a file has record components, and one component of the record is a file. c·
such a declaration, and give the form of the buffer variable of the inner file .
16.3.2 Consider the following declarations:
CONST
Upper=19;
TYPE
Material=(Brick, Block, Stone, Lumber) ;
VAR
St: SET OF Material;
Rd: RECORD
A: INTEGER;
B: Material;
C : Upper . . Upper
END
a) Give an execution state whose contents goes with these declarations, including an appropria.t""
value for each of the objects declared (not "?" but some value that might legally be assigned
For the remaining par ts of this problem, call you r st a t e S.
"
1111'~--
Preview
Pascal provides a convenient way to avoid writing the outer fields of a nested record description.
Records can be composed to achieve complex types. Consider the following declarations:
TYPE
Month= (NoMonth, Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec);
Date = RECORD
Mo: Month;
Day: 1 .. 31;
Year: INTEGER
END;
PRecord = RECORD
... ,
BirthDay: Date;
END;
VAR
Smith: PRecord;
Each Date has three selectors: Mo, Day, and Year. PRecords associate dates with personnel.
If a BirthDay were to be assigned to Smith, the sequence of assignments might be:
Smith.BirthDay.Mo := Feb;
Smith.BirthDay.Day := 28;
Smith.BirthDay.Year := 1960
Repeating the names of the fields in each statement is tedious and error-prone . The WITH
statement allows references like this to be factored into a statement heading. For example, the
previous assignments can be written:
WITH Smith.BirthDay
DO
BEGIN
Mo : = Feb;
Day := 28;
Year := 1960
END
The syntax rules for WITH statements appear below.
<with statement> ::= WITH <record variable list> DO <statement>
<record variable list> ::= <record variable list> , <record variable>
I <record variable>
<record variable> ::= <variable access>
16.5 Summary
Declarations for the aggregate data types SET, FILE, and RECORD take the form:
SET OF T
FILE OF T
RECORD
Fl: Tl;
... ,
Fn: Tn
END
Where T, Tv ... , Tn are types and F 1, ... , Fn are selector identifiers. Sets and files have components
of a single type; records may have components with different types. Set components must have
ordinal types other than INTEGER, although limitations on the number of components are imposed
by each Pascal machine. File components may be any type except another file or a record
containing a file component. Records have a fixed number of components specified by the
declaration. Set values may consist of zero or more components, but a maximum number is fixed by
the declaration. Files may grow to arbitrary lengths.
The members of a set cannot be explicitly selected, but the IN operator can test for the
presence of a given member. Only a single component of a file F can be accessed at any time during
execution by referring to the file's buffer variable Ft. Any component of a record can be selected for
reference or assignment by appending a period and the name of the component to a variable of the
appropriate type.
When choosing between the value and variable transmission mechanism for parameters with
aggregate types, considerations of security must be balanced against those of efficiency. Value
parameters are more secure because inadvertent assignments t o them do not change the execution
state where the call was made. However, value parameters require that separate copies of their
values are made in the program state of the called procedure each time the procedure is called. This
can be quite expensive for aggregate types. Thus, ordinal-type values are passed as value
parameters unless the called procedure uses them to return one of several results, but aggregate
values are often passed as variable parameters even if they are not updated by the procedure.
--
OT -
.....
CHAPTER 17
Chapter Preview
The control statements described in this chapter are not essential for programming. The effect
of each can be obtained using a combination of statements already described. However, each
statement is convenient in common programming situations, and the programmer who has them
handy often finds them a natural choice.
The CASE, FOR, and REPEAT statements of this chapter permit programmers to express
heir intentions more precisely and more conveniently than with the control s\atements of CF
Pascal. Where the IF statement selects one of two statements for execution, the CASE statement
selects one of what may be many alternatives. Like the WHILE statement, FOR and REPEAT
statements control iteration. In a FOR statement, the control is by a count, so FOR statements are
natural when the necessary number of repeated executions is known . In contrast, the WHILE
statement is appropriate when the number of iterations is not known, but a test condition for
stopping the repetition is available . WHILE iterations may be unbounded, but FOR iterations may
not. The REPEAT statement also controls an unbounded iteration, but where the WHILE test for
ermination occurs before each iteration, and is cast in the form of a condition required to continue,
t he REPEAT test occurs after each iteration, and is in the form of a condition required to stop.
Preview
It is common to use nested IF statements to select a single statement for execution from many.
The CASE statement can often replace such a nested IF with a gain in clarity and efficiency.
A CASE statement is formed from an ordinal-type selection expression and a list of statements, each
statement labeled by one or more constants of the same ordinal type . For example, in
CASE A > B OF
TRUE: Max :=A;
FALSE: Max := B
END
the selection expression is A > B of type BOOLEAN and the labels are the BOOLEAN constants
TRUE and FALSE . The selection expression is evaluated and the statement labeled with that value
(if any) is selected for execution. After the selected statement has finished execution, control is
transferred to the statement following the CASE statement. No constant label may be repeated, so
at most one statement can be executed. If no statement carries a label with the value of the
selection expression, the result of executing the CASE statement is undefined.
A CASE statement can be nearly simulated with nested IF statements. Consider the CASE
statement:
CASE Exp OF
Lla, Llb: Sl;
L2: S2;
• • e I
LN: SN
END
The IF statement:
IF (Exp = Lla) OR (Exp = Llb)
THEN
Sl
ELSE
IF Exp = L2
THEN
S2
ELSE
ELSE
IF Exp = LN
THEN
SN
has the same effect when the value of Exp is one of Lla, Llb, L2, ..., LN . However, when th e
value of Exp is not listed as a label, this IF statement acts as a null statement, but the CASE
statement is undefined. (Many Pascal machines implement the CASE statement incorrectly, making
it act like the IF statement for a missing label.)
The use of the CASE statement reduces the depth of the statement nesting and the need for
logical operators, making a program easier to read. The CASE statement is also more efficient than
the IF statement because the CASE selection expression is evaluated only once instead of for each
companson.
BEGIN
WRITELN('Adding operator encountered . ');
CASE Ch OF
'+': NumPlus := NumPlus + 1;
'-': NumMinus := NumMinus + 1
END
END;
I* I
' / ' : WRITELN('Multiply/Divide encountered.')
END
ELSE
NumOther := NumOther + 1
END
Notice the nested CASE statement in which actions are grouped and then split again.
Similarly, a few cases in an infinite type can be handled conveniently:
17.1.3 Exercises
17 .1.1 Explain how to construct a CASE statement that exactly simulates the effect of any given IF
statement.
17.1.2 The following declarations are in force for this question :
TYPE
Drink= (Coffee, Tea, Milk);
VAR
Ch: CHAR; Itg: 3 .. 33;
Enm: Drink;
EFile: FILE OF Drink;
In each part below there is at least one mistake . Locate and explain each one.
a)CASE SUCC(Tea) OF
Enm: Ch := 'X';
Coffee: Ch := 'Y'
END
I z I : • 0 0
END
b)CASE PRED(Sel) Of
X: •.• ,
Z:
END
7.1.4 Explain why the following pattern is not a useful one for the situation in which two cases
ave a common action, and one has an additional action:
CASE Ch Of
'A', 'B':
BEGIN
{Common action for 'A' and 'B'};
CASE 'A' Of
'A': {Additional action for 'A'}
END
END;
'C': {Other cases ... }
END
17.1.5 It has been proposed that the constants used in CASE statements to label each component
statement should be generalized to allow any expression as a label. Explain informally what the
meaning of such a generalized CASE statement should be, and as a part of your explanation,
criticize the proposal.
Preview
Iteration control in a FOR statement is by stepping through the values of some ordinal type,
and the current step value is available for calculation within the statement.
Let V be a variable of some ordinal type, and A and Z be expressions of the same type. The FOR
statement:
FOR V := A TO Z
DO
s
executes the statement S a number of times defined by the values of A and Z before the iteration
begins. Suppose that the value of A is p and the value of B is q at the outset. Then the first time S
is executed the value of V is p, the second time, V takes value SUCC (p), the third time,
more naturally than with the WHILE statement of Pr intDays in Section 15.5.2.
The INTEGER type is also a natural one to control with a FOR statement. The following
program might be the skeleton for producing student average grades; it reads a set of scores and
averages them.
PROGRAM AverageScore(INPUT,OUTPUT);
CONST
NumberOfScores = 5;
ClassSize = 3;
TYPE
Score= 0 .. 100;
VAR
WhichScore: 1 .• Number0fScores;
Student: 1 •. ClassSize;
NextScore: Score:
Ave, TotalScore, ClassTotal: INTEGER;
BEGIN {AverageScore}
ClassTotal := 0;
WRITELN ('Student averages: ') ;
FOR Student := 1 TO ClassSize
DO
BEGIN
TotalScore := 0;
FOR WhichScore := 1 TO NumberOfScores
DO
BEGIN
READ(NextScore);
TotalScore := TotalScore + NextScore
END;
READLN;
TotalScore := TotalScore*lO;
Ave := TotalScore DIV NumberOfScores;
IF Ave MOD 10 >= 5
THEN
already has the same value as E2. If this value were the last in an enumerated type, the final
evaluation of SUCC in the body would abort, where the FOR statement would not abort. Also,
assignment statements in the collection of statement S whose targets are variables in E2 would
change the number of iterations in the WHILE statement, but not in the FOR statement. The FOR
statement below iterates five times, even though First and Last change after execution begins:
First := 1;
Last := 5;
FOR Index := First TO Last
DO
BEGIN
First := First + 1;
Last := Last - 1;
WRITELN(Index, First, Last)
END
Execution
OUTPUT: 1 2 4
2 3 3
3 4 2
4 5 1
5 6 0
Some of these differences can be removed by using control variables that occur nowhere else in the
program (say T1 and T2), and by unrolling the first execution of the loop:
T1 := E1;
T2 := E2;
IF T1 <= T2
THEN
BEGIN
Index := T1;
S;
WHILE Index <> T2
DO
BEGIN
Index:= SUCC(Index);
s
END
END
Although this sequence of statements is closer to the meaning of the FOR statement, it still leav~
Index defined at the end of execution.
A direct definition of the FOR statement can be given as follows:
FOR V := E1 TO E2 DO S
where V is a variable of ordinal type T, has the meaning:
IF oR v : = E 1 To E 2 Do sj( t) =
(lvAR V: TIT oiVAR V: Tj)(t 0 ) iflsuccln- 1 {jE1j) (t) =IE21(t), where
t 0 =@] ( lv : = SUCC (V) I(t 0 _1))
t 1 =@](jv := E1j{t));
(jvAR V: TIT olvAR V: Tj)(t) if~(t) >IEzl(t).
By composing lVAR V : T IT a.nd lvAR V: Tj, the control variable is removed from the state, the;.
reinserted as i' 1t had been newly declared , with the effect that upon exit from the FOR statemen
In this area, a block letter may be represented as the set of positions in which a character is to be
printed, the other positions to r emain blank . For example, a block letter M might be represented by
the set constant:
[1,5,6,7,9,10,11,13,15,16,20,21,25]
t o indicate printing as follows:
M M
MM MM
MMM
M M
M M
Thus, the type SET OF 1 .. 25 can be used for the values that describe a block letter.
Two nested FOR statements (one to print each line and within it another to print each
column) can be used to print the letters.
Design Part 1
PROCEDURE PrintLetter(Ch : CHAR);
CONST
Height = 5;
Width = 5;
TYPE
PrintPos =SET OF 1 .. 25;
VAR
BLetters: PrintPos;
BEGIN {PrintLetter}
{BLetters assigned set of print positions};
{for each element of BLetters, print Ch}
END; {PrintLetter}
The value of Ch is used to select a n assignment statement that assigns the appropriate set of print
positions to BLetters .
- - -~ -
~ •
. ·--· . '
Design Part 1.1
{BLetters assigned set of print positions}
CASE Ch OF
'A': BLetters := [3,7,9,11 .. 15,16,20,21,25];
M M
MM MM
MM M
M M
M M
Function I 8
I := 1 1
8 := 8+I 8+1
I ·-
.- 8UCC (I) 8UCC(1)=2
8 := 8+I 8+1+2
I := N N
8 := 8+I 8+1+2+ ... +(N-1)+N
The table stops just before I is removed from the state then reinserted .) Thus the result is:
(8 := 8+1+2+ ... +(N-1)+N)
T he sum of the first N positive integers can be grouped from the beginning and end as follows:
1+N + 2+ (N-1) + 3+ (N-2) + . ..
where there are N72 pairs, since the terms are being removed two at a time from the original sum .
Each pair sums to N+1, so the result is
(8 := 8 + (N* (N+ l) ) DI V 2)
a.s required.
Preview
REPEAT statements control iteration with a test at the end of the loop body, a test that
terminates the iteration when TRUE.
The REPEAT statement provides for repetitive execution of a list of statements so long as a
BOOLEAN expression controlling execution evaluates to FALSE. The form is:
REPEAT
{Body statements}
UNTIL {controlling expression}
No BEGIN-END is required to bracket the statements of the loop body. Loop termination does no
occur as soon as the controlling expression becomes TRUE, but when it evaluates to true at the end
of the list of statements. Thus a REPEAT statement always has an iteration count of at least one.
The fragment:
Exp := TRUE;
REPEAT
WRITELN('This line is written to OUTPUT')
UNTIL Exp
appends a string to OUTPUT in spite of an expression controlling loop execution that is initially true.
17 .3.2 Exercises
17.3.1 Consider the programs:
PROCRAM RepeatTest(INPUT, OUTPUT);
VAR
Color: (Red, Green, Blue);
BEGIN {RepeatTest}
Color := Red;
REPEAT
Color := SUCC(Color)
UNTIL Color = Blue;
WRITELN('Color is now blue')
END. {RepeatTest}
PROGRAM WhileTest(INPUT, OUTPUT);
VAR
Color: (Red, Green, Blue);
BEGIN {WhileTest}
Color := Red;
WHILE Color <> Blue
DO
Color := SUCC(Color);
WRITELN('Color is now blue')
END. {WhileTest}
a) Are they equivalent?
b) Suppose that the initial assignment is changed to
Color := Blue
in each program. Are they equivalent now?
17 .3 .2 By writing an equivalent WHILE program and analyzing it usmg the WHILE verification
rule, show that the fragment R:
REPEAT
y := y - 1;
X := X + 2
UNTIL Y <= 0
computes the function:
Chapter Preview
The program modules described in Chapter 13 are important because they divide the complexity
of a program. In this chapter it is shown that they are also useful in dividing the calculation of
program meaning. The meaning obtained for a module may be used in an application, and the
calculation outside the module need not be repeated if hidden changes are made at the concrete
level. A method is given for verifying that a module correctly implements an abstract meaning.
When it was helpful to count in CF Pascal, lacking the natural data type for counting
I NTEGER), Chapter 13 created a module containing a collection of CHAR variables and the
procedures to use them for counting. All the cumbersome aspects of using characters to count were
hidden within this counter module, so that module users could ignore them . When the INTEGER
ype was introduced in Chapter 14, the counter module was no longer required. But from the user's
point of view there is little difference: · the counting operations using INTEGER are essentially the
same as those using the module . In this sense INTEGER is not a necessary feature of Pascal. (The
limitations of size imposed by the counter module were seen in Chapter 14 to be inherent in the
NTEGER type as well; they are limitations inherent in mechanical computation.)
The situation with a counter and INTEGER is not typical for Pascal or any other language.
The complicated data objects needed for difficult problems usually do not appear magically in a later
chapter. Thus programmers must construct the objects they need, but by hiding them in modules
t hey can be made so easy to use that they might as well be built in. Pascal features that were not
av ailable in Chapter 13 make module construction far easier and more powerful:
1. A TYPE declaration can be used for objects within the module, to hide their detailed
construction behind the type identifier.
2. A RECORD can be used in the TYPE to bind several objects into a new aggregate representing
an abstract object.
The module makes the TYPE construction details its secret, exporting the name only. Users of the
module can declare objects of this type, but are not supposed to know how they are constructed.
The rule of module construction is that for a TYPE secret, the TYPE identifier is available outside
the module, but the details of the TYPE definition are not available.
As an example, recall the counter example of Chapter 13, with some changes to take advantage
of TYPE declarations. The changes will allow users to have as many distinct counters as needed,
and to start a counter off at values other than zero. Users think of Counter objects as integer
values in the range 0-999, but within the module they are characters, the module's secret. By
declaring:
TYPE
Counter = RECORD
Hundreds, Tens, Ones: CHAR
END
users can create the needed characters without ever mentioning more than the TYPE identifier. For
example,
VAR
Cl, C2: Counter
would create two counters. To preserve the secret, the user must use only the names Cl and C2,
not (for example) C2. Tens, etc . To get the characters to the module, its procedures must be given
a Counter parameter:
Bump (C2 );
Va lue (C2 , Hd, Td , Od );
WRITELN('The value is ', Hd, Td, Od )
t o use C2 beginning at 103, and could use Cl for some different counting task .
The illustrated use of a Counter is the general case for modules. The module contains two
special procedures that get things started (here, StartAt) and report results (here, Value), an d
one or more procedures (here just one, Bump) that modify things in between. The pattern is to
begin with regular Pascal objects (here, characters) that are supplied to the starting procedure, the n
perform intermediate manipulations (without any detailed knowledge of the objects being
manipulated), and finally to obtain the result, again as regular Pascal objects. Of course, the initial
values might have been obtained with a READ statement or in some other way, and the final values
might have been used in some other way than with a WRITE statement . But to make use of them
outside the module requires that they be converted from/to standard Pascal data objects.
The use of a TYPE declaration also protects the module interface against changes in the data
structures used within the module . If the components of the TYPE were shown explicitly, the
declaration for Bump would have been:
PROCEDURE Bump(VAR Hundreds, Tens, Ones : CHAR)
but if the module were to switch to using INTEGER it would be:
PROCEDURE Bump(VAR Count: INTEGER)
such a change would require the user to alter not only the calls, but declarations of quantities to be
passed as actual parameters. On the other hand, if the TYPE declaration alone is changed:
TYPE
Counter = INTEGER
then changes are confined to the module; the user program declares and uses Counter objects
without caring tha t they have changed form.
Preview
The implementors of a module and its users have very different views of the module meanmg,
which are related but distinct.
It is important to keep the two wor lds, the one within a mod ule, and the one outside for users
of the module, distinct. We call the module interior the concrete world, and the user one outside the
abstract world . It is difficult to keep these apart, since t hey are intimately connected by a certain
idea that the user wants the module to realize. But if modules are to be useful in hiding the details
of an abstraction, the separation must be observed .
! Bump !I Bump I
{N·1, Manyl·?, {N .Hundreds·O, N. Tens·O, N. Ones•1
Many2·?, Many3·?} Manyl·?, Many2·?, Many3·?}
l
{N·1, Manyl·O,
Value !1 Value
If the procedures are doing the right things, the actions in the concrete world properly reflect those
in the abstract world step by step. The correspondence is the subject of Section 18.3.
Preview
Rational numbers (fractions) are a data abstraction not available in most computer languages.
They can be defined in a module, and used to solve a problem involving a fractional series.
Part Condition I1 I2
IF I2>0 I2>0 I2 I1 MOD I2
F I2>0 AND I1 MOD I2>0 GOD(I2,I1 MOD I2) 0
Part Condition I1 I2
IF I2>0 I2>0 I2 I1 MOD I2
F I2<=0 AND I1 MOD I2>0 Il MOD I2 I2 MOD (Il MOD I2)
The composite condition includes I2>0 AND I2<=0,which cannot be satisfied. Thus this case
contributes no pairs to the resulting function.
Part Condition I I1 I I2
IF I2>0 ... I2<=0
F I1>0 AND I2>0 I GCD(I1,I2) I 0
Again the composite condition requires the impossible of I 2, so there is no contribution to the
function.
Part Condition I I1 I I2
IF I2>0 ... I2<=0
F I1<=0 AND I2>0 I I2 I I1 MOD I2
This case also contributes no pairs to the function.
Part I Condition I I 1 I I 2
IF I2>0 I2<=0
F I2<=0
Thus the function is:
(I2<=0 -+ )
The union of these six functions yields the following:
(I2>0 AND I1 MOD I2>0-+ I1,I2 := GCD(I2,I1 MOD I2),0)
(I2>0 AND I1 MOD I2<=0-+ Il,I2 := I2, Il MOD I2)
(I 2<=0 -+ )
Some properties of greatest common divisors are needed to continue.
Property 1: GGD(X,Y) = GCD(Y,X)
Property 2: GCD(X,Y) = GCD(X MOD Y, Y)
Property 3: If X MOD Y = 0, then Y = GCD(X,Y).
Applying Property 1 to the first part function gives:
(I2>0 AND I1 MOD I2>0 -+ Il, I2 := GCD (I1 MOD I2, I2), 0)
(I2>0 AND I1 MOD I2<=0-+ I1,I2 := I2, I1 MOD I2)
(I2<=0 -+ )
Applying Property 2 next results in:
(I2>0 AND I1 MOD I2>0-+ I1,I2 := GCD(I1,I2) ,0) I
(I2>0 AND I1 MOD I2<=0-+ I1,I2 := I2, I1 MOD I2)
(I 2<=0 -+ )
Splitting the second function into two cases corresponding to I 1 MOD I 2=0 and I 1 MOD I 2<0
yields:
(I2>0 AND I1 MOD I2>0 -+ I1,I2 : = GCD (I 1, I 2) , 0)
(I2>0 AND I1 MOD I2=0 -+ I1,I2 := I2,0) I
(I2>0 AND I1 MOD I2<0 -+ I1 , I2 := I2, I1 MOD I2)
(I 2<=0 -+ )
By Property 3:
18.2.6 Exercises
8.2.1 Change the abstract comment for MakeRat to include the changes made to OUTPUT in the
ase Den=O.
~8 .2.2 Alter the action of MakeR at in the case of zero denominator to assign no values in this
!mpossible case; that is, to abort. Change both abstract and concrete comments to reflect what you
· ave done.
18.2.3 Aliasing of parameters had to be considered in AddRat. Is there a similar problem with
:>arameters Rat and NumRat in the operation BackRat? Explain.
18.2.4 Suppose that for some Pascal machine the value of MAX/NT is defined by CONST Mxi.
a) Explain how to alter the Rational module so that erroneous results like those shown in the
executions of HarmonicSeries never occur. [Hint: use a subrange type.]
b) Will any changes be required in HarmonicSer ies itself? Explain.
c) Change the abstract comments to reflect this change.
18.2.5 Consider a ltering the Rational module so that rational values may be externally described
as an integer part plus a fraction. For example, instead of passing the pair (5, 3) to the module
representing five thirds, a triple (1, 2, 3) could be passed representing one and two thirds.
• , . ~ ,'' I
a) Design an appropriate input-conversion procedure for this format, making no assumptions
about the fractional part. Do not change the internal form for Rational values, and use
MakeRat in the implementation if you can.
b) Discuss the question of whether or not to alter the internal form for Rational values to have
an integer, numerator, and denominator part. Describe a situation in which this form would be
the most efficient, and another in which it would not be efficient. Consider also the question of
making the internal form unique-when would that be wise, and when not wise?
c) Design an appropriate output-conversion procedure for the new format, assuming that th e
internal form for Rational values is not changed. Return the largest possible integer par
(that is, with a fractional part less than 1).
-
d) Show the changes in HarmonicSer ies that would be required to print the result in the for m
of an integer plus fraction, using your designs from a) and c) . .
Preview
The user of an abstract data type can employ its operations correctly if the "abs:" comments
can be trusted. It is shown how to verify that these do indeed reflect the actual actions taken
by an implementation. Furthermore, the abstract comments can be used to calculate the
meaning of user programs. Thus these comments are all that users need to know about an
abstract data type.
To illustrate the calculus of an abstract data type, we will use a particularly simp}
abstraction. The program below determines if the input contains an even or an odd number of
characters (counting line markers as single characters). It uses an abstract Switch object Sw
keep track of the characters read. Sw has abstract values Off and On. The abstract operations fo
a Switch are !nit, Flip, and Test. Init(x, y) initializes the value of Switch x from the CHA?..
value Y (On) or N ( Oj]) in y. Flip(x) changes x from Off to On and vice versa. Test(x, y) queri ~
the value of Switch x and sets y to the corresponding CHAR value.
PROGRAM OddOrEven (INPUT,OUTPUT);
{ (INPUT= <,w,R> and ODD(length(w)) -->
OUTPUT:= <'Input has odd length',,W>)
(INPUT= <,w,R> and EVEN(Length(w)) ~->
OUTPUT:= <'Input has even length',,W>) }
TYPE
Switch = RECORD
S: CHAR
END;
VAR.
Ch, SwitchOn: CHAR;
Sw: Switch;
functions returning Switch values but for the Pascal restriction to return only ordinal types.
3) The representation mapping onto the abstract value for identifier Sw is defined for two valuE
of the concrete identifier Sw. S: U (carried to On), and D (carried to Off). (Again, a~
enumerated type might have been a better choice, but we wanted to clearly distinguish t h
concrete values both from the abstract ones, and from the standard description.)
This example is too small to offer much abstraction, but OddOrEven is isolated from changes in t h
implementation. If an Australian had written the implementation, the values D and U wou l
probably be reversed, since Australian light switches are down when on, up when off. Such chang~
in the values for the . S component of the implementation do not alter the BEGIN statement of
OddOrEven at all.
!nit (X, 1 N 1 )
I \Test (X, R)
{concrete states}
I \
{concrete states}
\ I
jrnit(X, N
1 1
) I ITest(X,R) I
\
{concrete states} IFlip(X) I __,.
I
{concrete states}
This implementation diagram commutes: starting at the left with any standard description, the
standard description obtained at the right by following the upper (abstract) path is the same as the
one obtained at the right by following the lower (concrete) path. Within the diagram the two worlds
differ, but they begin and end in the same place. The statement that the diagram commutes is
exactly the same as the statement that the sequence of concrete operations correctly implement the
abstract sequence.
Commuting implementation diagrams can be used to reason about the relationship between the
concret e an d abstra ct worlds. No matter what initial standard description is chosen, and no matter
what intermediate opera t ions are used, the path th rough the abstract world should lead to the same
final standard description as the path through the concrete world. For the Switch module this
means considering all values of the parameter Ival passed to Ini t, and all sequences of
operations beginning with Ini t, continuing with any number of F 1 ip applications, and ending
; ',o - I •
.
with Test. For any such sequence the text of these procedures defines the resulting value for the
parameter Result in Test. The concrete comments summarize what should happen. There is no
"abstract code", but the abstract comments can be used to calculate Result from Ival. The
implementation is correct if and only if these two sequences of operations agree . /
It is a simple matter to check the particular implementation diagram above, which represents
one initial value for Ival and the simplest sequence of operations consisting of just one Flip .
Tracking the assignment statements in the concrete and abstract worlds, it can be easily seen that
the diagram does commute. However, to handle all diagrams in a complex module would be a
formidable task. Fortunately, the proofs can be decomposed by considering a correspondence
between the two worlds within each implementation diagram.
An implementation diagram must necessarily commute if the abstract and concrete worlds
"stay in step." That is, they begin together with a common (concrete) state. If each function in turn
maps to a state where there is still agreement, when they come back together in a common fin al
state, that agreement will still exist . To make a connection between the worlds within the
implementation diagram requires a new mapping called the representation mapping. This mapping is
in the mind of the person doing the implementation, and reflects the intuitive correspondence
between the two kinds of objects. It maps from the concrete to the abstract rather than the other
way around, because while it is essential that every abstract object have at least one corresponding
concrete object, it can happen that some concrete objects have no abstract counterparts, or th at
more than one concrete object corresponds to the same abstract one. If the representation mapping
were to go from abstract to concrete, under these circumstances it would not be a function. For
objects that lie in both worlds, that is, concrete objects that are not involved in t he module's special
TYPE declaration, the representation mapping is identity.
In the case of the Switch module, the representation mapping that the programmer had in
mind is Aswitc:h' which carries any concrete identifier X. S and its value to the abstract identifier X
and its value. Value U corresponds to On, and D to Off. That is,
Aswitc:h = {<s, t>: t = s except that X in t replaces X. S in s,
with t(X) = Off if s(X. s) = D, t(X) = On if s(X. s) = u}.
An implementation diagram can be decomposed using a representation mapping. For example, for
Switch, the diagram
{abstract states}
/
/nit (X, 'N ' )
/
{concrete states} Aswitc:h
~
IInit(X, 'N') I
~{concrete states}
Aswitch Aswitch
{abstract states}
Test (X, R)
I
Test (X, R) I
{concrete states}
is for the output-conversion mapping. If each of these diagrams commutes starting at the (lower)
left and ending at the (upper) right, then the composite implementation diagram must also commute.
All possible implementation diagrams can be handled by being slightly more general in the
proofs of the individual-operation diagrams. If instead of using the particular parameters X and
1
N 1 for Ini t, we use its formal parameters Sw and !val, then proving that the diagram
commutes proves that Ini t is a correct implementation for all possible parameter values. If the
diagrams for Flip and Test are similarly verified, then any implementation diagram beginning
with Ini t, continuing with any number of applications of Flip, and ending with Test, must
commute. That is, checking just three separate diagrams proves that the module is correctly
implemented.
·... . ! .J
This discussion leads to the following method for verifying the correctness of a module:
1) Find a representation mapping A that is identity on concrete objects not in the module '~
TYPE, that maps the implementation RECORD identifiers to a single abstract identifier, wi t
intuitively correct value correspondence.
2) Check that the diagram for each separate operation of the module commutes using A . This
can be done by verifying that the concrete comments correctly describes the implemente
functions, and then comparing with the function defined by the abstract comment.
The representation mapping is not part of the module implementation, but it is the most importan-
piece of information the person doing the implementation possesses. Correctness turns on th
existence of this mapping, making the individual diagrams commute.
{abstract states}
~
Init(Sw, Ival)
~
{concrete states} Aswitch
~..---:-----,
I Ini t (Sw, !val) I
~
{concrete states}
The second part of the composite condition is TRUE (U = U and D D) m the first and third
tables, and FALSE in the second and fourth tables, so the result is:
(Iva l = 1 Y 1 -+ S w : = On) (Iva l < > 1 Y 1 -+ S w : = 0 ff)
This is the same as lnit(Sw, Ival), and hence the diagram for Ini t commutes.
The diagram for Flip is:
l
Aswitch
l
Aswitch
{abstract states}
~{concrete states}
• Aswitch
~
Test(Sw,Result)
D
~
{concrete states}
18.3.4 Exercises
18.3.1
a) Give a formal representation function A for the Rational module of Section 18.2.1.
Show that the abstract comments for the following operations are correct, using the given concr e t~
comment and the representation of a).
b)PROCEDURE AddRat(VAR Result, Opl, Op2: Rational);
{abs: Result := Opl + Op2 }
{con: Result.Den,Result . Num .-
Opl.Den * Op2.Den,
Opl.Num * Op2.Den + Opl.Den * Op2.Num }
a) Write a concrete comment describing the implementation of the operation CatStr ing.
b) Give a representation function for this implementation of strings.
c) Verify the implementation.
18.3.6 Consider a procedure P without parameters and without any conflicts involving its loc a.
identifiers . P contains a commen t t hat cl a ims to give the box function of t h e body of th e proced ure_
. .f ~. -
PART V: PROGRAM DESIGN IN 0 PASCAL
The advanced facilities of 0 (Optimization) Pascal permit random data references and executicx
control. Two new data types in 0 Pascal permit random data references, arrays and pointers. _c.
variable of type :
ARRAY [ J ] OF T
is a bounded list of elements of type T, whose elements are indexed by values of an ordinal type •
For any type T, there is also a predefined, unbounded list of elements of type T indexed by values o
an unordered type called "pointer."
Program parts with arrays or pointers have part functions, but the formal derivation of th eR
part functions from the program text is beyond the scope of this book. Arrays and pointers ca.=
provide large speed improvements in a program, but they must be used with great care. They shou.~
be used only when their program part functions can be determined and verified at least informall.
We have already seen a similar situation with iteration statements. Any terminating WHIE
statement can be replaced by a group of nested IF statements, but the WHILE statement is
efficient (in terms of program text required), that the loops are worth the extra effort they requ ir=
for the determination of their part functions . Indeed, there is no mechanical way to prove t h:c
termination of loops, and therefore no mechanical way to verify the part function of a WHILE
statement. Nevertheless, with discipline, we can restrict ourselves to writing WHILE statemenT-
whose termination can be proved by ad hoc proofs. Similarly, even though a mechanical derivatior:
of part functions may not be possible when arrays and pointers are used, we can restrict ourselves
uses in which ad hoc derivations and verifications are possible.
GOTO statements permit transfer of execution control between almost any statements of ~
program; their program part functions are also beyond the scope of this book. However, GOT
statements are only introduced for efficiency reasons. They may be systematically removed tc
obtain an equivalent GOTO-free program that can be more easily analyzed.
0 Pascal contains a new data type, REAL, which approximates real numbers. Its advantage E
that, up to wide bounds, numbers of any size can be treated uniformly. Its disadvantage is th a·
numbers are rounded off in computation and round-off errors can accumulate to invalidate t h
results.
Chapter Preview
The array data type provides a collection of objects in which each member may be accessed as
quickly as any other. The size of the collection is limited and inflexible, but when size
restrictions are not important, arrays can provide a dramatic speed increase. Arrays are useful
for implementing abstract data types and for operations involving searching and sorting.
Arrays are fixed-size collections of objects that can be used to replace either functions (when
heir domains are small) or files (when they contain only a few components). Like functions, arrays
map elements from a domain of one type to a range of a possibly different type. With arrays
simulating functions, the value of a function can be "looked up" instead of computed each time it is
needed, often with a large saving in time. When an array simulates a file, access to any element
t akes a fixed amount of time, providing a large speedup when elements are needed in an
unpredictable order.
19.1 Arrays
Preview
An array is formed from two types: its components and its indices. The index values are used
to select component values. The power of arrays is in the ability to compute an index value,
then use it to obtain a component value, in a time that does not depend on either value or the
past history of array use.
19.1.3 Exercises
19.1.1 Modify the program Encryption of Section 19.1.1 so that
a) the blanks between words are encoded, so a code breaker would not have this clue to go by.
b) instead of padding with blanks, the program keeps track of which characters in Msg are a
valid part of the input, using a variable of type 1 .. Len to record the subscript "high water
mark."
19.1.2 Discuss the potential difficulty in modifying Encryption (Section 19.1.1) so that it can
handle arbitrarily long lines. Is this a practical difficulty?
19.1.3 In the Encryption program of Section 19.1.1:
a) Assume that the contents of the array Code defines a one-to-one mapping; that is, no range
element is duplicated. Modify Encryption so that using the same Code array (not one with
the code inverted) it can decode. That is, the input is a coded message, and the output is to be
the "clear" text .
b) Suppose that the contents of Code is not necessarily one-to-one. Write a procedure to check
for this property and print the duplications that exist.
c) Write a decoding program using Code as it exists, to handle the case where there may be
duplications in the range of Code. For each encrypted input, print all possible clear texts.
19.1.4 Which declarations and statements in the following Pascal fragment contain errors?
TYPE
AType = ARRAY (0 .. 4] OF BOOLEAN;
VAR
I, J: INTEGER;
A: ARRAY [I .. J] OF INTEGER;
B,C: ARRAY (0 •. 4] OF BOOLEAN;
D: AType;
E: ARRAY [0 .. 4] OF BOOLEAN;
F: AType;
G: ARRAY [1 .. 5] OF BOOLEAN;
BEGIN
IF B(I+J] = D[I*J]
THEN
WRITELN(C[PRED(O)]);
B := C;
B := D;
B := E;
D := F;
E := G
END
19.1.5 Each Pascal fragment below contains the identifier Qu. You are to deduce a possible type of
Qu from the context in each separate case. Express the answer as a type specification for the
declaration:
VAR
Qu: ?
Replace the question mark with the type required of Qu . For example, if you decide that one answer
is a subrange of CHAR between 3 and 6, fill in:
Preview
A data abstraction in which the concrete objects are finite can be implemented very efficiently
using arrays.
The array data type implements finite lists with efficient, random access to any list member.
The file type provides access to any member of a file, of course , but only, in sequential order. If the
desired member is not near the head of the file's future list when it is needed, many READ
statements will be required to obtain it.
19.2.3 Exercises
19.2.1 Explain why the Ini tializeSF operation is necessary, in addition to ResetSF and
RewriteSF, for the SmallFile data abstraction of Section 19.2.1.
19.2.2 Add abstract comments to the procedure headers of the SmallF i le data abstraction of
Section 19.2.1.
Preview
Because array elements may be accessed in any order with no time penalty, new methods of
sorting and searching become available.
Sequences of values can be stored in arrays as well as in files. The major differences between
files and arrays are the number of components and the ability to access components. The number of
components in a file is not specified by the programmer, but grows during execution time to a limit
imposed by the Pascal machine, while the number of components of an array is defined in the
declaration of the array and cannot be changed during execution. Each component of an array is
immediately accessible, but only a single component of a file (the first from the future list) is. A file
has a built-in mechanism (EOF) for detecting its end, but only part of an array may contain valid
values, and this must be explicitly provided for by the programmer.
' ,, \
19.3.1 Searching an Array
An array can be searched as a file is: by examining each component in order. However, when not all
components of the array should participate, a way must be found to mark the end of valid data in
the array. A clever way is to use the element to be found as a sentinel, placing it in the array at the
end to stop the search.
Design Part 2
PROGRAM SentinelSearch (INPUT, OUTPUT);
{INPUT contains integer values:
Key, i1, ... , iN, N=ArrSize-1 -->
((There is a J, 1<=J<ArrSize, iJ=Key -->
OUTPUT :=Key & 'found at position J')
(For all J, 1<=J<ArrSize, iJ<>Key -->
OUTPUT :=Key & 'not found')) }
CONST
ArrSize = 11;
VAR
Arr: ARRAY [1 .. ArrSize] OF INTEGER;
Index: INTEGER;
BEGIN {SentinelSearch}
{get values for Arr and Key};
Index := 1;
WHILE (Index <= ArrSize) AND
(Arr[ArrSize] <> Arr[Index])
DO
Index := Index + ~;
IF Index < ArrSize
THEN
WRITELN(Arr[ArrSize], ' found at position', Index)
ELSE
WRITELN(Arr[ArrSize], 'not found')
END.
The value to be found is read directly into the last position of Arr:
Design Part 2.1
{get values for Arr and Key}
READ(Arr(ArrSize]);
FOR Index := 1 TO ArrSize-1
DO
READ(Arr[Index]);
Assembling these two parts:
Execution
INPUT: 9 0 1 2 3 4 5 9 8 7 6
OUTPUT: 9 found at position 7
SentinelSearch takes no advantage of an array's ability to access components out of order .
A technique called binary search can improve the execution speed of the search if the data list is
sorted. The idea is to probe into the list at the halfway point, discover on which side the sought
value lies, then probe that side at its halfway point, and so on until only one possible position
remains. The desired value must lie there or it is not in the array . For example, consider searching
the following array for the value 70:
The first probe is made at position 4, the midpoint of the entire array. Since Arr [ 4] is less than
t he value sought, the lower portion of the array is eliminated and the next probe is made at 6,
halfway through the upper half of the array. Again the lower portion is eliminated, leaving only
Arr [7] to be examined. Thus three comparisons were necessary to search an array of seven values.
In geqeral for an array with 2n-1 elements, the elements can be split in half at most n times before
reaching a subsequence of the array containing only a single element. Thus instead of having to
make at most 2n-1 comparisons to locate a value, no more than log 2(2n) = n comparisons are
required.
Design Part 3
PROGRAM BinarySearch (INPUT, OUTPUT);
{INPUT contains integer values:
Key, i1, ... , iN, N=ArrSize;
AND (For all K, 1<=K<ArrSize, iK <= i(K+1) ) AND
(i1<=Key<=iArrSize) -->
((There is a J, 1<=J<=ArrSize, iJ=Key -->
OUTPUT :=Key & 'found at position J')
(For all J, 1<=J<=ArrSize, lJ<>Key -->
OUTPUT :=Key & 'not found')) }
CONST
ArrSize = 7;
VAR
Arr: ARRAY [1 ArrSize] OF INTEGER;
Low, High, Key: INTEGER;
BEGIN {BinarySearch}
{get values for Arr and Key};
Low := 1;
High := ArrSize;
IF (Key >= Arr[Low]) AND (Key <= Arr[High])
THEN
{Search Arr[Low] to Arr[High] for Key}
ELSE
WRITELN(Key, ' not in range of values')
END. {BinarySearch}
Design Part 3.1
{get values for Arr and Key}
READ (Key);
FOR Index := 1 TO ArrSize
DO
READ(Arr[Index]);
The binary search portion repeatedly divides the interval between Low and High in half until at
most a single element remains. The value of this element is compared to that of Key.
N( .!.+.!+1.+. .. + NN1 ) .
2 4 8
_ .!. .! l_ N-1 _ .! l_
Let T - ( + + +. .. + N ) . Then 2T - (1+ + +. .. +
2(N-1)
,.., ). Performing the subtractio
2 4 8 2 4
2 T- T in the following peculiar way:
19.3.3 Exercises
19.3.1 Write a recursive version of BinarySearch (Section 19.3.1}, using the fact that once the
search array is reduced in size, searching it is an instance of the same problem.
19.3.2 An insertion sort is a good technique to use when data values must be placed in order as they
arrive . An array holds the values received so far, in sorted order. When a new value arrives, its,
proper position in the array is found and it is inserted, moving the existing values out of the way.
Write an intera~tive program that prompts for an integer as input, keeps the integers in an arra_
sorted by insertion sort, and can print the array on demand .
19.3.3 A pseudo-key called a hash code can be computed from a search key, and used as an index to
build or search a table . As long as all search keys hash to unique codes, a single comparison (to see
if a table entry exists) will determine if the table contains a value equal to the search key. If a table
is being built, the search key can be inserted at the position selected by its hash code. Man.
different algorithms exist for computing hash codes, e.g., for arrays of characters we might add the
ordinal representation of the first several characters and use the sum modulo the table size as the
hash code index.
Preview
PACKED arrays are stored so that they occupy less space m the ·Pascal machine.
Multidimensional arrays are useful for storing tables and matrices.
l
Word[Exp]
l
Word[SUCC(Exp)]
t
Word[SUCC( ... Exp ... )]
The number of SUCC applications in the last subscript is the size of the unpacked array.
Similarly, the UNPACK procedure moves values from a packed array to an unpacked array.
For the declarations above,
UNPACK(PWord,Word,Exp)
acts like:
FOR J := PLb TO PUb
DO
Word[J-PLb+Exp) := PWord[J)
The number of components unpacked is the size of the packed array. The first component of the
packed array is moved to the unpacked array at subscript Exp, and so on.
!
Word[Exp]
!
Word[SUCC(Exp)]
!
Word [SUCC ( ... Exp. ·.. ) ]
A special form of packed data has already been seen in CF Pascal: strings. A string constant
of length Str ingLength is treated as if it has a type:
PACKED ARRAY[l •. StringLength] OF CHAR
Thus the type of 'HELLO' is
PACKED ARRAY[l .. S] OF CHAR
while that of 'GOODBYE ' is:
PACKED ARRAY[1 .. 7] OF CHAR
Just as these string constants can be written to TEXT files, so can any PACKED ARRAY .. .
OF CHAR. However, although it might seem sensible, strings may not be read from TEXT files. The
relational operators can also be applied to strings, but only if the string lengths are the same. Thus,
'HELLO' and 'GOODBYE ' cannot be compared because their lengths are not the same, but string,
'HELLO ' can be compared with 'GOODBYE' since both have length 7. The comparison is the
usual lexicographic one: characters are compared from the left, and the first different pair
determines which string precedes the other, using the collating sequence for the Pascal machine. For
example,
·· ·· . . _ . ---_, ~ \ .
Design Part 6.1.1
PROCEDURE ReadWord(VAR Fin: TEXT; VAR Result: WordType;
VAR Success: BOOLEAN);
{ReadWord reads the next word from Fin, places the
result in Result, and sets Success to indicate
whether or not a word was read}
VAR
WideWord: ARRAY[l .. MaxWordLength] OF CHAR;
Ch: CHAR;
BEGIN {ReadWord}
Ch := I I ;
WHILE NOT (EOF(Fin} OR (ChIN ['A' .. 'Z', 'a' . . 'z']))
DO {skip characters not in words}
READ(Fin,Ch);
IF NOT EOF (Fin}
THEN
BEGIN
{read word, putting first MaxWordLength chars
in WideWord};
{pad WideWord with blanks};
PACK(WideWord,l,Result);
Success := TRUE
END
ELSE
Success := FALSE
END; {ReadWord}
Care must be taken in the next design part because end of file may be encountered while reading
characters into WideWord.
Design Part 6.1.1.1
{read word, putting first MaxWordLength chars
in WideWord}
ChCount := 0;
WHILE NOT EOF(Fin) AND (ChIN ['A' .. 'Z', 'a' .. 'z'])
DO
BEGIN
IF ChCount < MaxWordLength
THEN
BEGIN
ChCount := ChCount + 1;
WideWord[ChCount] := Ch
END;
READ(Fin,Ch)
END;
WideWord is padded with blanks before packing it into Result.
Design Part 6.1.1.2
{pad WideWord with blanks}
FOR ChCount := ChCount+l TO MaxWordLength
DO
WideWord[ChCount] := I
If Word is not in Table, InsertWord installs it in lexicographic order, using an insertion
sort. Initially Word is pla ced at the end of Table so a sentinel search can be used.
where the first subscript indicates a row and the second subscript a column. Thus the number of
subscripts is often referred to as the dimension of an array. Arrays can have any number of
dimensions. For example, a three-dimensional array with
TYPE
ThreeD =ARRAY [0 .. 3, 0 .. 3, 0 .. 3] OF Chip
could be used to represent a three-dimensional tic-tac-toe board of 4 planes of 4 X 4 boards.
Multidimensional arrays are often used to represent matrices of numerical values. Suppose
that three matrices M1, M2, and M3 are declared as follows:
CONST
Sub1 = 1;
Sub2 = 3·,
Sub3 = 2·,
TYPE
Matrix1 = ARRAY [1 .• Sub1, 1. :sub2J OF INTEGER;
Matrix2 = ARRAY [1 .. Sub2, 1 . . Sub3] OF INTEGER;
Matrix3 = ARRAY [1 .. Sub1, 1 .. Sub3] OF INTEGER;
VAR
M1: Matrix1;
M2: Matrix2;
M3: Matrix3;
The product of M1 and M2 is defined to be a matrix whose type is Matrix3 and whose element
[I ,K] has the value
Sub2
I; (M1 [I , J] * M2 [ J, K])
J =1
for all I in the range 1 . . Sub1 and all K in the r ange 1 .. Sub3. The second dimension of Ml
·must be the same as the first dimension of M2 for the product to. be defined . Suppose that M3 is to
be computed as the product of Ml and M2. Element M3 [I, K] is formed by multiplying the
corresponding elements of row I of M1 by those of column K of M2. If Ml and M2 have the values
shown below:
'
~ -.
y... , ' "'
Execution
INPUT :1 2 3 4 56 7 8 9
OUTPUT: 40 46
19 .4.3 Exercises
19.4.1 In many computers the basic unit of data is larger than a character, so several characters
an be placed within one unit. The machine operations mostly move whole units, but special
operations are available for extracting and replacing part of a unit. For such a machine, PACKED
arrays of CHAR are stored with several characters in each unit, and unpacked arrays with one
haracter in each unit. In terms of this implementation, explain each of the following:
a) why more data can be stored in the machine if packed arrays are used than if unpacked arrays
are used.
b) why moving a single character from one position to another in a packed array takes more time
, than if the array were unpacked.
c) why the PACK operation always uses the entire packed array, but only part of the unpacked
array may be used.
d) why packed-array elements may not be bound to variable parameters.
19.4.2 It is difficult in Pascal to write a procedure declaration that will handle as actual
parameters constant strings. As an example, one might like to pass:
PROC ('HELLO I);
PROC ( I GOODBYE I )
but no such procedure PROC can be written.
a) Explain why.
b) Describe the best way around the problem that Pascal allows.
c) Does this difficulty come up with variables as well as constants? Explain.
19.4.3 Modify the program Index (19.4.1) so that each printed word is followed by a line number,
of the line on which it first appeared. Number lines from 1 as they are input.
19.4.4 Write a mod~le for sparse table, a collection of character strings positioned in rows and
columns, where most positions in the table are blank. Declare a TYPE Table in which all the
strings are stored end-to-end in a CHAR vector in arrival order, and a list of INTEGER values
locates the starting and ending positions of each string, and its position in the table. This locator
list should be kept in an order so that the table can be printed by going through it sequentially.
Each Table will require a maximum of 500 characters total string storage. The following
types and operations are required:
CONST
SMax = 500;
TYPE
String = RECORD
Data: PACKED ARRAY[l .. SMaxJ OF CHAR;
{The characters of the string in order}
EndPoint: O .. SMax {The subscript of the
final character; 0 for empty string}
END;
Loc = RECORD
Row: INTEGER;
Col: INTEGER
END; {Row and Column values begin at 1}
.
, ... . # .......-..·
PROCEDURE ClearTable(VAR AllNew: Table);
{This routine must be called first for each table
to be used. It can be used to initialize the data.}
FUNCTION TableEntryOK(It: String; Pos: Loc;
VAR Tab: Table): BOOLEAN;
{Enter It into Tab at Pos, returning TRUE if no entry
was there, FALSE if the entry there was overlaid.}
FUNCTION PrintTable(Tab: Table): BOOLEAN;
{Print the existing table without disturbing it.
Use as many columns and rows as needed.
Make the columns equal-sized in an 60-space width.
Left justify the entries, and where entries are
adjacent, there must be at least one space between
them. If entries would collide or go outside the
60 spaces, truncate them as necessary, and in
this case return FALSE. Otherwise, TRUE.}
Write an application of this module in which strings are input with their row and column subscripts,
placed in the table, then printed. For this application, the input data (in order: row, column,
string):
3 2 Corner
1 3 #
5 1 Jumped Over
4 1 This string is not too long to fit
5 3 The Quick Brown Fox Jumped Over the Lazy Dog
7 2 THE END
should print as:
#
Corner
This string is not too long to fit properly
Jumped Over The Quick Brown Fox
THE END
19.4.5 Matrix addition is defined component by component. That is, two matrices can be added
only if they are the same size, and each component of the sum (which is also of this size) is the sum
of the corresponding components of the two. Write a procedure that adds two matrices.
Preview
Most program analysis is made more difficult by the presence of arrays. Subscripts that may be
calculated make each array access a potential reference to any part of the whole array.
Array programs in which the subscript values are fixed can be analyzed using each array element as
a variable. This is just the approach used for RECORD types, in which the variables named using
the field selectors are treated like any other variable. But array elements are unlike fields of records
in that subscripts need not be constant, and when a subscript value is calculated in a program, the
element to , which it refers may range over the whole array. The program meaning may well be
different for different subscript values, and the only known way to perform the analysis is to treat
the whole array as a variable, with its composite val ue.
19.5.1 Exchange
The following fragment was used m ExchangeSort to switch the values of two elements in the
array Vals:
{exchange Vals[Start] and Vals[Min]}
Save: = Vals[Start];
Vals [Start] := Vals[Min];
Vals[Min] :=Save
The trace table for this fragment is shown below:
That is, the effect of these three statements is given by the concurrent assignment:
Save,Vals : = Vals[Start],
((Vals; Start:Vals[Min]); Min:Vals[Start])
To prove that these statements do what they were designed to do requires showing that the values
a t subscript positions Min and Start are switched. But because an array is involved, that is no.t
all that must be shown. The values at all other subscript positions must be unchanged. Without
t his additional demonstration, the statements could do arbitrary damage to the array and still be
considered correct. In terms of the initial state, the statement that the values are switched can be
given using the array value from the trace table. The value v1 of
The final line in this table is tricky to calculate, since it involves replacing I by I+ 1 and Arr by
(Arr; I : I) in :
The final table has the condition !>Size and an identity assignment:
The combined results are identical to I, with the first case broken into two:
(I <Size -+
I,Arr := Size+1, (( ... ((Arr;I:I);I+1:I+1) ... );Size:Size) )
(I=Size-+ I,Arr := Size+1, (Arr; Size:Size) ) I
(I>Size -+ )
Although the meaning of the WHILE statement has been demonstrated to be I, the form of the
final array value:
( ( . . . ( (Ar r ; I : I ) ; I + 1 : I + 1 ) . . . ) ; Size : S i z e)
does not give much insight into the values of the individual array elements. What is needed is to
calculate the value
( ( . . . ( (Ar r ; I : I ) ; I + 1 : I + 1 ) . . . ) ; S i z e : S i z e) [ J]
for an arbitrary value of J that lies between the values of I and Size. Applying the fundamental
array property until the term with subscript J is reached, the value is J. Thus the WHILE
statement has set the array value to an identity mapping in which each subscript contains its own
value.
Again it is evident that verifications with arrays require much effort, corresponding to the
flexibility and power they provide for optimizing execution . When an array is used in a controlled
way, for example as part of the implementation of a module, then the power may be worth the cost,
because the complications of analysis are confined to the proof that the module is correct. But
19.5.3 Exercises
19.5.1 Simplify the value:
(((X;I:X [ I-1]);J: (X;K:Y) [K]);I-1:Z) [I]
using the fundamental property of arrays.
19.5.2 For the fragment that exchanges two values from array Vals (Section 19.5.1), try to
construct a trace table using
Vals [1] Vals [2] Vals[10]
as the column headings. Explain what goes wrong.
19.5.3 Suppose a programmer means to set one array element to zero, and writes
A[I] := 0
In subsequent debugging it is discovered that the subscript should be I+ 2, but the first statement is
erroneously left in the program:
A[I] := 0;
A[I+2] := 0
The programmer now wishes to prove that the program is correct.
a) In terms of the complete value for array A, write the statement that subscript position I+ 2
has been zeroed, and prove that it is so.
b) Explain why the proof of a) is insufficient.
c) Give a statement of what should be proved, and show that it does not hold for the the
erroneous program.
19.5.4 Show that the Pascal fragment
A [K] : =
A [K] + K;
K := K + 1;
A [K] : = A [K] - K;
K := K - 1
computes the function
A[KJ, A[K+1] := A[K] +K, A[K+1] -K-1
(Don't forget to show that the other array elements are unchanged by the fragment.)
19.5.5 Show that the Pascal fragment
K := 0;
X [0] : = 1;
WHILE K <= N
DO
BEGIN
K := K + 1;
X[K] := X[K-1] * K
END
computes the function
For all I O<=I<=N, X[I],K := I!,N
(Hint: do the analysis using the whole array value, and go to the given form only at the end.)
19.5.6 Show that the Pascal fragment
CHAPTER20
LINKED STRUCTURES
Chapter Preview
Many important data structures grow and change as they are used by a program during
execution. These linked structures can be kept in Pascal array storage, but they are more
naturally implemented using Pascal pointer types.
All Pascal data types except files are static: the size of stored data is fixed by the declaration.
Ordinal and set data types have trivial data structures of a single value. Records and arrays
comprise multiple values, but the structure and number of components is fixed. Files are of one
generic structure, the sequence, and can vary dynamically only in length.
However, many problems are best solved with data structures that vary in size and shape as
the solution progresses. For example, an arithmetic expression such as:
(3+9) 1 (12- (3* 2))
can be represented as the tree:
/~
+
/\
3 9
~* 12
/~
3 2
I I
/~ /~
+ 1: ~ -
3
/~~ 9 12 6
/~
12 6
(First the multiplication, then the addition has been performed.)
12
/\ 6
(The subtraction, then the division, yield the final result 2.)
Each tree in the sequence can be viewed as an instance of a single data structure varying
dynamically during the solution.
Preview
An array of records can be used to hold a link~d structure by adding a "pointer" field to the
record, to record the subscript values of other records in the structure.
Static array structures can be viewed as dynamic if their components move relative to each other.
For example, suppose a sequenc~ of records is stored in an array, and a sorted version of the data is
required. ExchangeSort or MergeSort (Section 19.3.2) can be used to rearrange the records,
moving the entire record based on all or part of its contents. (The portion of a record that actually
participates in the ordering is called the sort key.) But a better way to sort the data is to add a new
field to each record and place in it the subscript of the alphabetically following record. The records
are chained together by these subscript pointers, and by following the chain, they can be obtained in
sorted order. The advantage over physically sorting the re cords is that only the chain pointers need
be changed. When the records are large, particularly when the sort key is a small part of a large
record, chaining is much faster than moving the records.
-----~-
Suppose that the following data are stored in this structure:
Consider the Name field of each record as the key. The most straightforward way to get a sortec.
version of the data is to move the records about, to eventually produce the array:
The second way to get a sorted version is to add a 0 .. Max field Next to each record and declare a
new variable of type 0 .. Max, say First. Then, leaving the records in place, set the Next field of
each record to the subscript of the one following in alphabetical order, and First to the subscript
of the initial record in that order {4):
The last record is flagged with the sentinel 0, an impossible subscript for this array indicating that
nothing follows. Now to obtain the record subscripts in alphabetical order of their contents, begin
with First and follow the chain:
Index :=· First;
WHILE Index <> 0
DO
BEGIN
{Index is the subscript of the current
item in alphabetical sequenpe. Use
it to access whatever fields are needed.}
Index. : = FRees [Index] . Next
END
The series of values taken on by Index inside the WHILE statement is thus 4,1,3,2-the sorted
order.
The chain pointers can be drawn in to give a graphic representation of the linked structure.
The subscript value in each record is replaced with an arrow pointing to the record with that
subscript:
First Name
Miller Smith Plane Jones
(rest of
000 000 00 0 000
record)
However, a simpler diagram results if the array sequence is abandoned in favor of the sort sequence.
Fir st
I ... Next
Name
Jones Miller Plane Smith
(rest of
0 00 0 00 000 000
record)
A structure in which records contain pointers to other records is called a linked list. The links,
stored in each record's Next component, provide a sort sequence by specifying, for each record, the
location of its alphabetic successor. When there is no link (indicated here by a 0 value), the record is
the last in the linked list.
Records may be added to a linked list without moving even the links for most of the existing
records. For example, if a new record with the Name RushDDD should be added to the alphabetic
sequence, the array can be expanded to place it without regard for the sequence, and the links
rearranged.
M Q p
I ------
t
M Q p
Some care must be taken when linking the first part of the list to the new value since this value rn a_
become the new first element of the list. If Prev begins with value 0 and does not change durin
the search, the new element is at the beginning of the list, and must be pointed to by First r ath e~
than by another list element . No similar caution is required should the new element fall at the en
of the list, because the 0 value in the old final record would be transferred to the new record as if i
were an onward pointer.
Design Part 1.1
{link Arr[Index] into existing list}
Prev := 0;
Curr := First;
{find values for Prev and Curr (if they exist)
such that
Arr[Prev] .Key <= Arr[Index] . Key <= Arr[Curr] .Key};
Arr[Index] .Next := Curr;
IF Prev = 0
THEN
First := Index
ELSE
Arr[Prev] .Next := Index
A Boolean vari~ble Found is introduced to stop the search as soon as Prev and Curr have
acquired their values.
/~
+
/\
3 9
\---_•
12
/~
3 2
Node 1 2 3 4 5 6 7 8 g
Left 2 4 6 0 0 0 8 0 0
Riqht 3 5 7 0 0 0 g 0 0
Oo3 I + - *
Val 3 g 12 3 2
where node N is stored in position N of the array Exp. An operation can be performed if an
operator node is linked to two operand nodes. The nodes numbered 2 and 7 have this property .
When an operation is performed, the resulting value can replace the operation node. This is
accomplished by erasing the node fields corresponding to the Left and Right links and the
operation, and assigning the result of the operation to the Val field. For example, if the operation
at node 2 is carried out, the representation of the expression would change to:
Node 1 2 3 4 5 6 7 8 g
Left 2 0 6 0 0 0 8 0 0
Riaht 3 0 7 0 0 0 9 0 0
Op3 i - *
Val 12 3 9 12 3 2
If the operation at node 7 is carried out as well, the resulting data structure is:
Node 1 2 3 4 5 6 7 8 g
Left 2 0 6 0 0 0 0 0 0
Rioht 3 0 7 0 0 0 0 0 0
Oo3 I -
Val 12 3 g 12 6 3 2
..;..;:· '
.·
Node 1 2 3 4 5 6 7 8 9
Left 2 0 0 0 0 0 0 0 0
Riqht 3 0 0 0 0 0 0 0 0
Op3 I
Val 12 6 3 9 12 6 3 2
Node 1 2 3 4 5 6 7 8 9
Left 0 0 0 0 0 0 0 0 0
Riqht 0 0 0 0 0 0 0 0 0
Op
Val 2 12 6 3 9 12 6 3 2
A program could be designed for evaluating arithmetic expressions stored in the structure
above. To recognize operator nodes, a character (say I) could be placed in the Op field of each
operand node. The recognition of operator nodes that can be immediately evaluated can then be
accomplished by searching for an Op value that is not I, but whose Left and Right links go to
Op values that are I. Some efficient ways to construct and use expression trees. are suggested in the
Exercises.
node N (making k+1 total links), which points to the node that points to N. Doubly-linked tree5
permit easier navigation , but are larger and more difficult to construct and modify, since more links
must be maintained.
20.1.5 Exercises
20.1.1 In the example of the linked list containing five names at the end of Section 20.1.1:
a) give a single assignment statement that will delete Miller from the list.
b) Will an assignment similar to a) do as well for any other record in the list? Explain.
20.1.2 Suppose that names are stored in an array of PACKED arrays, as in PRecs in Section
20.1.1.
a) Modify ExchangeSort (Section 19.3.2) to sort the names.
b) How can a name consisting of the usual first, last, and middle parts be stored in an array of
PACKED arrays so that names may be easily sorted and searched in the ways that might be
required by a personnel office?
20.1.3
a) Devise a single line of test data for InsertionSort (Section 20.1.2) that will exercise all of
the following situations:
The inserted item goes at the beginning of an existing list.
The inserted item goes at the end of an existing list, and the following inserted item goes at the
end of that list.
The inserted item goes somewhere between the beginning and end of the list.
b) This kind of testing is called special-values or boundary-values testing because it exercises
special cases in the data structure. Explain why for InsertionSort, an item falling at the
end of the list isn't really a boundary value, but one falling at the beginning is.
20.1.4 Modify InsertionSort (Section 20.1.2) so that after it has constructed the list, it reads a
second line of input containing an integer P, and prints record number P in the list. Be sure to
handle error cases.
20.1.5 Program animation consists of adding graphic capabilities to a program so that as it
executes, a pictorial record of its actions are produced. Animation is valuable in understanding how
algorithms work, and in debugging programs. Think about animating InsertionSort (Section
20.1.2). What should be printed at which points in execution? Give an example of what you would
like the output to look like for a typical addition of one record.
20.1.6 Consider the sample expression tree at the start of Section 20.1.3.
a) What is the value of Exp[Exp[Exp[l] .Left] .Right] . Val?
b) What array expression will access the character * ?
20.1.7 Using an array whose index type is
TYPE Color = (Violet, Indigo, Blue, Green, Yellow, Orange, Red)
create a data structure that can be used to read in a list of colors and sort them into "rainbow
order" (the order of the enumerated t ype above) using a linked list. The color names are in the inpu-t
as initial letters on a single line, but the full names should be printed. For example, the response to
the input tYIGt should be
Indigo
Green
Yellow
20.1.8 Write a program to do expression evaluation by searching the structure of Section 20.1.3 for
an operation that can be performed, performing it, and repeating the search.
2
/~3
4
~5
/~7
6
8 9
~/\
10 12 13 11
~
14 15
If these position numbers are used as the subscripts in which records are stored, no pointers need be
stored; instead, the links can be computed. For a rf!cord stored at position N, the location of its left
subtree is position 2N, and its right subtree is position 2N+l. Furthermore, the backpointer from
node M is to node M+2.
a) Give the declaration of the suitable 2-tree data structure for arithmetic expressions.
b) Write a recursive evaluation program for the structure of a).
c) Write a nonrecursive depth-first search procedure that examines a 2-tree stored as in a) by first
going all the way down the left subtree, then back to the first node with an unsearched subtree
and down its left subtree, etc.
20.1.12 Polynomials can be represented by linked structures. Declare a type and variable of that
type to represent a polynomial in the variable x with integer coefficients. Give the values that
represent x 2 - 2x + 1.
20.2 Pointers
Preview
Pascal pointer objects make precise the idea of linking between parts of a structure. In
addition, they allow programs to create and use structures whose size truly vanes as the
program executes.
Pascal has a collection of data types to support the construction and manipulation of linked
structures, called pointers. Pointers are unusual data objects, because they have associated with
their identifiers two values. One value is a Pascal data item of the usual kind, an example of a
Pascal TYPE. The other value is something new, a kind of "locator" for the data value. The locator
value is really what the pointer is all about. The reason for introducing this new locator between
the identifier and the value is that in a linked structure operations are often not on the values stored
in the structure, but only on objects that allow the values to be found, later. For example, in
building the list of values for an insertion sort, most of the processing involves the links between the
values, not the values themselves. In fact, the links are called "pointers" because their values are not
the data, but only help to find the data . We will call the "locator" value the pointer value (or
..
--
'
. .,..
sometimes, for clarity, the value of the pointer itself). The value pointed at will be called the target
value. A pointer value for identifier Ptr is thus represented by the diagram:
Locator Target
-+---~1 I
One pointer type is declared by prefixing a type identifier with an upward arrow, "t". The
rules of type syntax are extended as follows:
<new type> ::= <new ordinal type> I <new structured type> I
<new pointer type>
<new pointer type> ::= t <type identifier>
For example:
TYPE
Refint = fiNTEGER;
Refint is the type of a pointer that locates an integer value. A pointer variable is declared using a
pointer type, e.g.:
VAR
Pint: Refint;
The operations for any pointer type are the relationals = and <>, and assignment. When
pointers are assigned, it is the value of the pointer itself that gets copied, not the target value. Two
pointers are equal if their pointer parts are the same; otherwise, even though they may point to
target values that are the same, the pointers are not equal. There is one constant value for a
pointer itself: NIL. When a pointer variable is assigned this constant, e.g.,
Pint := NIL
there is no target value to which Pint points. Pointers can be given values only by assignment (to
NIL, or to another pointer with a value), or with the NEW statement, which creates both the object
and the locator values. The built-in procedure NEW is given a pointer variable as actual parameter.
For example, the statement
NEW(Pint)
creates a new pointer-itself value for Pint, which locates (points to) an INTEGER target value (as
yet unknown). The target of pointer Pint is accessed by suffixing Pint with an upward arrow:
Pintf
This is often called dereferencing a pointer value. Thus the target of a pointer has the same form as
a file buffer. The rules of syntax are extended as follows to include targets:
<variable access> ::= <variable identifier> I <buffer variable> I
<component variable> I <identified variable>
<identified variable> ::=<pointer variable> t
<pointer variable> ::=<variable access>
The DISPOSE built-in procedure takes as parameter a pointer expression.
DISPOSE (Pint)
releases the storage for the target pointed at by Pint. The pointer itself is not released, nor is it
necessarily set to NIL. Thus, NEW and DISPOSE are used in pairs separated by the computation
that uses a target object:
G-D
Some Pascal machines set the value of a pointer to NIL as a part of the DISPOSE statement, but if
this is not done (as in the diagram above), the pointer is said to be a dangling reference. It points to
a target that has ceased to exist. When a pointer value itself is NIL there is no target, but the
programmer can test for this condition:
IF Pint <> NIL
THEN
{Use Pintt .. . }
However, only careful programming, keeping track of which pointer values are valid, prevents the use
of dangling references.
.
' J". .......~. · .·
. -
.··i
VAR.
Pint: fiNTEGER
is encountered. The state then becomes
. {Ch·X, Pint·?}
in the usual way. Following the statement
NEW(Pint).
it becomes
{Ch•X, Pint·f1, 1 f·?}
because the first pointer name is used for the value of Pint itself, and the target to which it points
is as yet _unknown. Finally,
Pintf := 99
yields
{Ch•X, Pint·f1, 1 t·99}.
In simple cases like this, Pintf·99 might just as well have been put in the state instead of 1 t·99,
and Pint left out altogether. But two complications make our notation essential: pointers may be
duplicated, and there may be no identifier for the target value.
First, consider a duplicate pointer. If the declaration is
Pint, Qint: fiNTEGER
then the code
NEW(Pint);
NEW(Qint);
Pintt := 99;
Qintf := Pintf
leads to the state
{Pint·fl, Qint·f2, 1f·99, 2f·99},
which has the diagram:
Pint Qint
lt
~
But with the same declarations,
2t
0
NEW(Pint);
Pintt := 99;
Qlnt := Pint
yields the rather different state
{Pint·fl, Qint·fl, 1 f·99},
with diagram:
It~
In the second case, the pointer values themselves, that is, the values of Pint and Qint, are equal,
and so are the object values (Pintt and Qintt); in the first case only the target values are equal,
and the notation shows this.
Second, consider a target value without an identifier.
TYPE
ContP = RECORD
Int: INTEGER;
Pint: fiNTEGER
END;
VAR
TopP: tcontP;
BEGIN
NEW(TopP);
TopPf.Int := 93;
NEW(TopPf.Pint);
TopPf.Pintf := 0
END
leads to the state
{TopP=f1, 1 f.Int=93, 1 f.Pint=f2, 2f==O}
in which the target 2f with value 0 has no declared identifier. The diagram is:
TopP 1t
If a linked list is being constructed, this situation is carried to the extreme that no target but the
first item in the list has an identifier; the rest have only pointed-at places.
. I·
' . ' '
One way to create enough places to put the pointer values created by NEW statements is t
situate the pointer within the target object that is created. For example,
TYPE
Node = RECORD
Value: INTEGER;
Next: fNode
END;
is a record containing a pointer to its own type. The identifier Node is used within its owr:
definition, one of the rare occurrences in Pascal where definition need not precede use. In fact, a
type identifier could be associated with fNode before Node was defined:
TYPE
NodePtr: fNode;
Node = RECORD
Value: INTEGER;
Next: NodePtr
END;
Using this data structure, the linked list of INTEGER values
~r~r~r J' 20 :
can be created. Access to the entire linked list will be provided by a pointer variable declared:
VAR.
FirstPtr: NodePtr;
The first list item and the access link can be implemented by:
BEGIN
NEW(FirstPtr);
FirstPtrf.Value := 1
END
That is, FirstPtr points to a record of type Node whose Value field has value 1, and whose
Next field is unknown:
FirstPtr
---+----!~I 1
To establish the second link, another NEW statement must be executed, but it should place the new
pointer in the just-created record:
BEGIN
NEW(FirstPtrf.Next);
FirstPtrf.Nextf.Value := 2
END
The list is now:
· - --
'
- ~·
FirstPtr
,.. 1 ...... 2
I
?
FirstPtr
Thus 19 elements of the list remain as garbage, and F irstPtr is a dangling reference. In order to
reclaim the storage occupied by the list structure, more care is required. The list must be traversed
and a DISPOSE statement executed for each pointer, in the proper order so that nothing escapes.
.
------==-=
. --~ -
.
Design Part 2.1.1
{find values for Prev and Curr (if they exist) such that
Prevt.Key <= NewPtrt.Key <= Currt.Key}
Found := FALSE;
WHILE (Curr <> NIL) AND NOT Found
DO
IF NewPtrt.Key > Currt.Key
THEN
BEGIN {move to next element}
Prev := Curr;
Curr := Currt.Next
END
ELSE
Found := TRUE;
inally, the elements are printed by making NewPtr reference each list element in sorted order.
Design Part 2.2
{print list starting with FirstPtrt.Key}
NewPtr := FirstPtr;
WHILE NewPtr <> NIL
DO
BEGIN
WRITE(NewPtrt.Key);
NewPtr .- NewPtrt.Next
END;
WRITELN
--- -~
PROCEDURE Insert(VAR Ptr: Tree; Data: CHAR};
{Insert the value of Data into the tree rooted at Ptr.
Maintain the lexicographic ordering of the tree.}
BEGIN {Insert}
IF Ptr = NIL
THEN
BEGIN {Create leaf with value Data}
NEW(Ptr};
Ptrf. Ch := Data;
Ptrf.LLink := NIL;
Ptrf.RLink := NIL
END
ELSE
IF Ptrf.Ch > Data
THEN
Insert(Ptrf.LLink,Data}
ELSE
Insert(Ptrf . RLink,Data}
END; {Insert}
For example, the tree corresponding to the input string tCBDt is:
B
/~D
...······················· ························...
(The dotted links represent NIL pointers.) C was first placed at the root. Then B went left and D
right. Suppose now that another C arrives, followed by F . The result will be:
c
/~D
...........
B
... , """" ...........
... , """"
c F
/ ·················..... / ····················..
Printing the characters in alphabetic order is accomplished by traversing the tree in the proper
order. The rule for any node is to print everything in its left subtree, then the contents of the node,
then everything in its right subtree. This also has a concise recursive implementation:
20.2.6 Exercises
20.2.1 Each Pascal fragment below contains the identifier Qu. You are to deduce a possible type of
Qu from the context in each separate case. Express the answer as a type specification for the
declaration:
VAR
Qu: ?
Replace the question mark with the type required of Qu. For example, if you decide that one ans.wer
is a subrange of CHAR between 3 and 6, fill in:
VAR
Qu: '3' .. '6'
If there are several possible types, explain briefly.
a) Qut := 'X'
b)IF Qu <>NIL THEN WRITELN('Continue')
20.2.2 Give a type declaration for T to make the following Pascal fragment legal:
VAR
P: T;
BEGIN
Ptf.Q := 'X'
END
20.2.3 Suppose that a linked list of INTEGER values is to be created, and the choice is to be made
between an array with INTEGER subscripts and a pointer implementation . Explain why the
pointers are "safer" in that it is impossible to confuse the values with the pointers, but that in the
array implementation this confusion is allowed.
GOTOSTATEMENTSANDSTRUCTUREDPROGRAMS
Chapter Preview
The presence of GOTO statements affecting the flow of control in a program makes it
unstructured, and in most cases more difficult to understand and analyze. GOTO statements
may have a place for dealing with exceptions and for improving program efficiency, but in most
cases they must be eliminated for t he sake of understanding.
A GOTO statement can transfer control to almost any statement in a program. Programs with
GOTO statements can be difficult to understand because the transfers destroy the hierarchical,
part-function structure of the other Pascal statements. As a result, even though a Pascal program
with (any number of) GOTO stat ements has a program function, that program function cannot be
calculated one statement at a t ime. Instead, a program with GOTO statements must be analyzed in
larger pieces, considering many statements simultaneously. Because of this intertwining of
statements, the analysis is more complex, more error-prone. In short, a program with GOTO
statements can be a real "can of worms" to unravel, and though the analysis is theoretically possible,
it is impractical.
Preview
IF B IF B
THEN THEN
s GOTO 1;
ELSE T; GOTO 2;
T 1: S·'
2:
WHILE B 1: IF NOT B
DO THEN
s GOTO 2
S;
GOTO 1;
2:
Few programmers would choose to give up the Pascal IF and WHILE statements in favor of these
alternatives. The GOTO statements destroy the textual structure of the program intended by the
programmer . Programs with GOTO statements are difficult to read because control can be
transferred either forward or backward, and to learn the conditions under which a labeled statement
may be executed, it is necessary to consider each GOTO statement that references its label.
However, the GOTO statement is a natural way to express an intended break in the control pattern,
most often to escape from an iteration early.
[~ : !] and [: !]
would appear as the input string:
t3 7 2 1 4 9 2 4 3 1 8 2t.
In this example, 12 integers are expected. If only 7 are supplied, the first matrix is defined, but not
the second. The following partial design deals with the possibility of insufficient data. A BOOLEAN
variable DataOK is used to indicate that enough data were present to define both matrices.
21.1.4 Exercises
21.1.1 A label must be a digit sequence whose length is 4 or less, yet the syntax given in Section
21.1 does not control the length. Is this necessa rily done with a context condition, or can syntax
equations be written for exactly those sequences that are labels?
21.1.2 What mistakes involving labels and GOTO statements occur in the following program?
Preview
The analysis techniques of the program calculus depend on having structured programs
containing no GOTO statements. When a program is unstructured it _can be converted to a
structured program for analysis.
A Pascal program with the hierarchical part-function structure of the program calculus is
called a structured program. Conversely, a program that lacks this structure (because it uses
unrestrained GOTO statements) is called an unstructured program. The program calculus presented
in this book applies only to structured programs, and all methods known for calculating the meaning
of unstructured programs are much more difficult to use. Thus the way to analyze an unstructured
program is to first convert it to structured form.
LA::
END
END
where Ini t is a constant with the value of the first label L 1 in U and Exit is similarly the value
of the final label LA:+ 1 .
The statements to be inserted in the CASE statement are modifications of the ones from U,
depending on the form of the original statement.
When the statement S with label L in U is a null statement, an assignment statement, a
READ or WRITE statement, or a procedure statement, place the following at L within the CASE
statement:
BEGIN
S;
LabelValue := Af
END
where Af is the label of the statement following Sin the original program U.
When the statement in U is an IF statement:
L: IE' B
THEN
T:
ELSE
E:
Then within the CASE statement at label L place:
IE' B
THEN
LabelValue := T
ELSE
LabelValue := E
and similarly if there is no ELSE part to the IF statement. (The statements labeled T and E will
transformed and also appear in the CASE statement.)
When the statement in U is a WHILE statement:
L: WHILE B
DO
BEGIN
D:
END;
E:
Then within the CASE statement at label L place:
IE' B
THEN
LabelValue := D
ELSE
LabelValue := E
The easiest case is the one for which the whole scheme was invented. When the statement in r.,-
lS:
L: GOTO Af
the statement at L within the CASE is:
LabelValue := Af
The program thus constructed from U is structured, since no GOTO statements were
introduced, and those of U were all replaced. The control flow of U is exactly reproduced in the
sense that each test performed in U and each statement executed in U are done in the same order,
but between statements Label Value is computed, and the WHILE and CASE statements direct
the execution based on this value. However, since Label Value was not present in U, these
additional calculations cannot alter any results U produced, and hence the structured program will
exactly reproduce any input-output behavior of U.
The structured program produced from an unstructured original is not an example of good
programming style. However, it can be analyzed using the program calculus, and it may prove
possible to transform it into a simpler equivalent form.
-
-~~----
---~~
~
- '
.. . -
21.2.2 A Structuring Example
In order to illustrate the structuring const c ·on, consider the program:
PROGRAM GreatCalculat ionDo_ e (:N? , OUTPUT);
LABEL
10, 20, 30, 40, 5 0, 60 , 70, 80, 90, 100;
VAR
I1, I2: INTEGER ;
BEGIN
10: READ(I1, I2) ; GOTO 20 ;
20: IF (I1 > 0) AND (1 2 > 0) THEN GOTO 80
ELSE GOTO 100 ;
30: IF I1 < I2 THE N GOTO 70 ELSE GOTO 50;
40: IF I1 = I2 THEN GOTO 90 ELSE GOTO 30;
50: IF I1 > I2 THEN GOTO 60 ELSE GOTO 40;
60: I1 := 11 - 12 ; GOTO 40;
70: I2 := I2 - Il; GOTO 40;
80: WRITE('DATA IS ' , Il:3, I2:3); GOTO 40;
90: WRITELN(', ANSWER IS', I1:3); GOTO 10;
100 :
END.
The program uses GOTO statements to present a neat appearance, with all READ, IF, assignment,
and WRITE statements grouped together. But what the program does- its program function-is
another matter . If its WRITE statements are to be believed, it takes two input integers and
produces a third integer as result. But what is the result? If GreatCalculationDone were
structured, its program function could be obtained step by step using techniques of the program
calculus. But that will not work here. Instead, the entire set of statements is locked together with
all those GOTO statements, so that no part can be separated out for analysis and study .
GreatCalculationDone is a real can of worms. It doesn't help very much to perform trial
executions, or to work out their execution tables. One quickly learns that the program echos its
input in pairs, and terminates when either input is not positive, but it isn't obvious how the result is
obtained.
The structured version of GreatCalcul ationDone is obtained by adding labels to the
unlabeled statements and carrying out the construction of the last section. Since there are no
procedures, only the BEGIN statement of the program must be structured. First the additional
labels:
PROGRAM GreatCalculationDone1(INPUT, OUTPUT);
LABEL
10, 15, 20, 23, 27, 30, 33, 37, 40, 43, 47,
50, 53, 57, 60, 65, 70, 75, 80, 85, 90, 95,
100;.
VAR
I1, I2: INTEGER;
BEGIN
10: READ(I1, I2); 15: GOTO 20;
20: IF (I1 > 0) AND (I2 > 0) THEN 23: GOTO 80
ELSE 27: GOTO 100;
30: IF Il < I2 THEN 33: GOTO 70 ELSE 37: GOTO 50;
40: IF I1 = I2 THEN 43: GOTO 90 ELSE 47: GOTO 30;
50: IF I1 > I2 THEN 53: GOTO 60 ELSE 57 : GOTO 40;
60: I1 .- I1 - I2; 65 : GOTO 40;
70: I2 := I2 - I1; 75: GOTO 40;
80: WRITE('DATA IS', I1:3, I2:3); 85: GOTO 40;
90: WRITELN(', ANSWER IS', 11 :3); 95: GOTO 10;
---- - . --------"---
--1\,
., ..
WRITELN(', ANSWER IS', I1:3)
END
ELSE
L1 := 100
END
END.
The variable L1 now only controls exit from the WHILE statement, and can be eliminated by
changing the WHILE condition to
WHILE (I1 > 0) AND (I2 > 0)
which reflects the only way that the iteration terminates. There is one complication. The
termination test follows a READ statement, but moved into the WHILE it would precede that
READ. A way to deal with this is to duplicate the READ statement before the WHILE statement
and move it to the end of the DO statement:
PROGRAM GreatCalculationDone6(INPUT, OUTPUT);
VAR
I1, I2: INTEGER;
BEGIN
READ (I1, I2);
WHILE (I1 > 0) AND (I2 > 0)
DO
BEGIN
, WRITE('DATA IS', I1:3, I2:3);
WHILE I1 <> 12
DO
IF 11 < 12
THEN
12 := 12 - 11
ELSE
11 := 11 - 12;
WRITELN(', ANSWER IS', 11:3);
READ (I 1 , I 2)
END
END.
It is interesting to compare the practically inscrutable unstructured program
GreatCalculationDone with the structured version GreatCalculationDone6. The latter
program can be seen to calculate with successive pairs of input integers. In order to discover what
the calculation is, it only remains to study the inner WHILE statement W:
WHILE I1 <> I2
DO
IF 11 < 12
THEN
I.2 := I2 - I1
ELSE
Il := Il - I2
It is not obvious at first glance that W ever terminates. However, in the IF statement, the
greater of I 1 and I 2 is decremented by the lesser of these on each iteration. Thus one of the two
values always decreases, and neither can go to zero, since they start out unequal. This decrementing
process bounded below cannot continue indefinitely, so the iteration must terminate.
The termination argument shows that I wlhas the form:
21.2.3 Exercises
21.2.1 Eliminate the GOTO statement from the following program, distorting its structure as little
as possible.
PROGRAM Search{INPUT,OUTPUT);
{Read a list of integer values from input and print
the distinct values and the number of times they occur}
LABEL
10;
CONST
MaxSize = 10;
TYPE
!Type= l .. MaxSize;
VAR
Vals: ARRAY [!Type] OF INTEGER;
Searches : ARRAY [!Type] OF INTEGER;
CurrSize: O .. MaxSize;
Key: INTEGER;
I: !Type;
PUT(OUTPUT);
IF INPUTt I
THEN
WRITE (I I) ;
10 :GET (INPUT)
END;
WRITELN
END. {Format}
Execution
INPUT :one. two. three.///fou.r/f.ive/six.
OUTPUT:one. two. three.
four fiv~ e six.
21.2.3 Show that if every procedure a.nd function of a Pascal program has a closed BEGIN
statement, then the program 's ma.in BEG statement is necessarily also closed.
21.2.4 In the construction of a structured program from an unstructured one in Section 21.2.1,
explain why the statements from within IF and WHILE statements are not inserted at the labels for
the IF and WHILE.
21.2.5 In the construction of Section 21.2.1 , suppose that the labels placed on the original program
statements are all in numerical order , increasing throughout the program text.
a) Explain why the original program will not necessarily produce the same results if the order of
statements is changed by moving a complete labeled statement.
b) Explain why moving labeled statements within the CASE statement of the structured version
does not change the program's meaning.
21.2.6 The construction of Section 21.2.1 covers only the CF Pascal language with GOTO
statements added. This exercise is to show that the construction can be extended to all of Pascal.
a) Extend the construction to cover CASE statements.
b) Extend the construction to cover REPEAT statements.
c) Extend the construction to cover FOR statements.
d) Explain why the additional data types of D and 0 Pascal do not enter into the construction.
21.2.7 Prove that for every CF Pascal program, an equivalent program can be written using exactly
one WHILE statement. What constructions not in CF Pascal are required to write this equivalent?
21.2.8 Suppose a program is structured, but the scheme for structuring is applied to it anyway.
Compare the quality of the original with that of the new structured program.
21.2.9 Construct an execution table for the input
t 12 16 -1 -1t
a) to the program GreatCalculationDone (Section 21.2 .2).
b) to the program GreatCalculationDone2.
21.2.10 Instead of creating the structured GreatCalculationDone6, use a single GOTO
statement to transform GreatCalculationDone5 (Section 21.2 .2) into a program not containing
the variable Ll, but in which the READ statement need not be duplicated.
21.2.11 Demonstrate formally that g = GCD for the WHILE statement W analyzed at the end of
Section 21.2.2. In addition to the properties of GCD listed in Section 18.2.4, the following may be
helpful:
GCD(X,X) = X,
GCD(X, Y) = GCD(X- Y, Y) for X> Y.
REAL NUMBERS
Chapter Preview
Pascal provides an approximation to real numbers in the data type REAL. As with type
INTEGER, there may be errors when t he size limits of the Pascal machine are exceeded; in
addition, a new source of difficulty arises in roundoff errors.
Real numbers may be represented in Pascal with an explicit decimal point or in floating-point
(scientific) notation . In floating-poin t not ation, a number is thought of as described by two parts, a
real number m called the mantissa and an integer e called the exponent or scaling factor. For some
positive integer base b, the number value is
m X be.
The base is usually a power of two (often sixteen) in computer hardware used for the Pascal
machine, but the examples in this chapter will be given in base ten- they are easier to understand
a nd the ideas do not depend on the base. The mantissa is restricted to a range that will make the
floating-point representation unique. In the usual base-ten scientific notation this range is [1, 10),
t hat is, all reals r such that 1 < r <10. Using the letter "E" to separate mantissa and exponent,
some examples are:
In most Pascal machines the space available for mantissa and exponent of a floating-poin t value is
limited, which leads to potential arithmetic errors.
22 .1 Type REAL
Preview
The REAL data type is convenient for processing which requires fractional values, or where
values cover a wide range.
X TRUNC(X) ROUND(X)
5.3 5 5
7.5 7 8
-6.3 -6 -6
-9.7 -9 -10
ABS and SQR have the same meaning as the INTEGER counterparts. EXP computes the
-
exponential function f(x) = ez, and LN computes the natural logarithm (for positive arguments).
Since
en In b = (eln b)n = bn,
a power function like
X * X * X * X * X {5th power of X}
can be calculated with
EXP(5 * LN(X)) {5th power of X}
SQRT computes the square root of its positive argument and is otherwise undefined. ARCTAN, SIN,
and COS compute these trigonometric functions for arguments expressed in radians.
c>.
~ --
'
~ .... '
'
. . ~ ..
22.1.3 Compound Interest and Simulation
The use of type REAL will be illustrated by a simple example and a more complex one .
If an amount of principal P is invested at interest rate r for T years, compounded m times
year, the balance accrued will be
P(l + r/m)mT.
Given input containing the principal, interest rate, number of compoundings per year, and years, t~
following program prints a table of balances at the end of each year .
PROGRAM Compoundinterest(INPUT,OUTPUT);
{Reads principal (real), interest rate (real),
number of compoundings (integer), and
years of investment (integer). Computes
balance at the end of each year.}
VAR
Balance, IRate: REAL;
Periods, Years, I: INTEGER;
BEGIN {Compoundinterest}
READ(Balance,IRate,Periods,Years);
WRITELN('Principal',Balance:11:2);
WRITELN('Rate ', 100*IRate:5:2, '%');
WRITELN('Compounded',Periods:4, 'times');
WRITELN;
WRITELN('Year New Balance');
FOR I := 1 TO Years
DO
BEGIN
Balance := Balance *
EXP(Periods*LN(1+IRate/Periods));
WRITELN (I: 2, ' ',Balance: 11: 2)
END
END. {Compoundinterest}
Execution
INPUT :10000.00 0.08 365 20
OUTPUT:Principal 10000.00
Rate 8.00"
Compounded 365 times
Il : = Rl;
R2 : = I 2;
WRITELN(Il- TRUNC (Rl *I 2 )) ;
WRITELN(Il < Rl);
WRITELN(Il + Rl < Rl ) ;
WRITELN(Il + Rl - I2 );
WRITELN(Il / Rl + I2 )
END
22.1.2 Explain the rationale that lies behind the Pascal restriction that the controlled variable of a
FOR statement may not be REAL.
22.1.3 Investigate what your Pascal machine does for REAL input/output, in particular:
a) What is the default format and field width for REAL output?
b) Does the problem of end of file with INTEGER input (discussed in Section 14.3.2) exist for
REAL input as well?
c) What happens on output if the field width is inadequate?
d) Are leading or trailing decimal points allowed on input?
e) What happens on output if the number of digits after the decimal point is specified to be more
than the actual precision available?
f) Is it possible to read in a REAL value, then write it, so that the result appears different than
what was in the input?
22.1.4 Write a procedure taking one REAL parameter that prints this value in "dollars and cents
format"; that is, with a leading $ at the left (no spaces offset), and the number divided into groups
of three digits separated by commas, and rounded to two digits following the decimal point. For
example, 1.234566666E5 should appear as $123,456. 67 .
22.1.5 Design a module that implements the data abstraction Reali in which a real number is
represented using type INTEGER.
a) Represent the real number by two INTEGER values, one for its mantissa and one for its
exponent. Implement the operations of input conversion (from two INTEGER parameters),
multiplication, addition, and output conversion (print directly in scientific notation). Arrange
to give correct results so long as it is possible to keep both values within the bounds of
MAXINT, represented in the program by a constant declaration.
b) Repeat a), but for a representation in which the two values are the part of the real number
before and after the decimal point.
22.1.6 Change the representation of type Rational in Section 18.2.1 from two INTEGER values
to a single REAL value, and make the necessary changes in the operation implementations. Explain
the changes that will be required in HarmonicSer ies (Section 18.2.3) because of the new
representation.
22.1.7 Design a module to implement type Complex. A complex number z = a + ib where i =
Y(-1) should be represented by REAL components a and b. Implement the operations of addition
and multiplication for Complex. Devise an appropriate input/output format and write a program
using your module which reads two complex numbers and prints their sum and product.
Preview
The REAL data type is usually implemented using special hardware in a Pascal machine,
hardware in which both mantissa and exponent are limited in size, and so subject to overflow .
Conversion between number bases for input/output can introduce additional errors.
Scientific calculations usually make use of real numbers, and the speed with which the
necessary arithmetic can be executed may be the limiting factor in deciding whether or not a
problem can be solved in a reasonable time . (Speeds are measured in "megaflops"-millions of
floating-point operations per second; 10 megaflops is a fast machine .) However, the payment for
speed is a potential for error: the way REAL quantities are stored for fast arithmetic limits both
mantissa and exponent in size. This means that the range of real values is limited, and so is the
precision. When overflow occurs in the range, t he situation is much like that for type INTEGER.
The range of REAL values is greater, but that r ange can be exceeded, particularly by repeated
multiplication or division . The difficulty with precision is more subtle, and falls under the heading of
roundoff error.
Because the mantissa is limited, only a certain number of digits (thinking in base ten) can be
stored. If a value requires more digits than there are available, it cannot be exactly represented and
must be "rounded off" to the nearest value that is available. The most important cause of roundoff
error is the natural loss of precision in intermediate computations. For example, consider the sum :
Equivalent Values
base two base ten
.1 .5
.01 .25
.001 .125
.0001 .0625
.00001 .03125
.000001 .015625
Conversion of the fractional part proceeds by repeatedly finding the largest value in the base-ten
column and subtracting it from the remainder to be converted, at the same time adding the base-two
equivalent to the result .
Rounded to six "bigets" {usually called bits) the result is 11.1011 = 0.111011X22. Errors of this kind
are called representational errors.
The approximate nature of REAL computations implies that programs should not test REAL
values for equality. The following Pascal fragment may never terminate because successive additions
of 1/10 to 0.0 may never yield the exact value 1.0.
RValue := 0.0;
WHILE RValue <> 1.0 DO
BEGIN
... {loop body}
RValue : = RValue + 1 / 10
END
It is much better to control the loop with the test:
RValue < 1 . 0
The usual course of scientific computation is to read input data (and express program
constants) in base ten, convert to the internal base of the Pascal machine, carry out many
arithmetic operations, then convert the result back to base ten for output. Each conversion and
The Actual value column is the result of a perfect calculation, but using the rounded value
(Computed value column) from the previous step. The last column (Local error) is the difference
between the actual and rounded result for each step. However, the complete story is not told in this
table, because it takes no account of the accumulation of errors throughout the whole computation.
Imagine that the "perfect" results of each operation (signified by a symbol within a circle) were
available. Then the following table could be constructed:
The last two columns are the difference between the composite rounded result (Computed value in
the previous table) and the perfect value, expressed as a magnitude (Total error) and as a fraction of
the perfect value (Relative error).
Errors accumulate in complex and unexpected ways even in this simple calculation. In step 4,
the relative error jumps from less than 1% to more than 24%. The local error in step 4 is 0, but a
previously accumulated error is amplified because the difference of two nearly equal numbers is
computed. Steps 4 and 5 have large relative errors, but the final result of step 6 does not. This is
the happy result of the dominance of the perfect value 7.4 in the computation.
Initial Data
1 -1 -2 -3 -4 -5
qi 0.16 0.86 -2.9 7.4 2.3
Computed Data
step i 1 2 3 4 5 6
value qi 0.37 1.2 2.8 -0.10 -0.23 7.2
Furthermore, the six steps had operation and operand indices given by the table below.
These tables conveniently record all the information needed to analyze roundoff error.
22.2.3 Exercises
22.2.1 Find a number that can be exactly represented in base ten with two-digit precision, but when
converted into base two with four-bit precision, then converted back to base ten with two-digit
precision, is not the original value . Explain why this sort of thing does not happen in Pascal input-
output conversion.
22.2.2 Using real values with rounding to two decimal digits, construct examples to demonstrate
that the usual associative and distributive laws of arithmetic do not hold. What lesson can be
drawn from these examples by t he Pascal scient ific programmer?
b2 - b 2 +4ac 4ac c
4a 2 = 4a 2 =-;
Thus, after computing x 1 with the Pascal fragment
Rt := SQRT(B*B- 4*A*C);
Den := 2*A;
IF B < 0
THEN
Xl .- (Rt - B)/Den
ELSE
Xl := -(B + Rt)/Den
x 2 can b~ computed with:
X2 : = C/(Xl*A)
Check to see if the problem that arose in (a) persists in this method, and explain.
22.2.5 Consider polynomial evaluation by a different method than Horner's rule. Compute the
needed powers of the variable x by repeated multiplication, then multiply each by its coefficient, and
finally add the terms beginning with the highest-power term and continuing down to the constant
term.
a) Obtain the formula for the final accumulated error when the polynomial
0.16x3 + 0.86x2- 2.9x + 7.4
from Section 22.2 is evaluated in this way.
b) Compare the results with the Horner-rule analysis of Section 22.2.2.
22.2.6 Prove that when the polynomial
Cnxn + Cn_ 1xn- l + ... + C 1 x + C 0
is evaluated using Horner's rule the accumulated roundoff error is
Preview
Centralizing information in CONST and TYPE declarations is a way to allow easy program
change, particularly when these declarations are used to control module implementations.
However, it is all too easy to build unwarranted assumptions into a program so that extensive,
difficult changes are required.
In Section 19.2.2, a Stack was represented using an array and an index to its top element.
The declarations hiding the size and representation were:
CONST
Depth = 20;
TYPE
EltType = CHAR;
Stack RECORD
Val: ARRAY [! .. Depth] OF EltType;
StackTop: O .• Depth
END;
Using a constant declaration for Depth keeps the INTEGER 20 out of the further declarations
and out of the Push and Full operations. A single change can therefore expand the Stack size,
and it is very likely that the changed module will perform correctly if the original one did. Similarly,
by making E 1 tType a TYPE instead of spreading CHAR throughout the module , it is easy to create
(say) a Stack of SET values in place of characters. The changes that are thus enabled are far-
reaching. For example, to stack up to 2500 personnel records, the only changes required are:
~--- 01 - --
-:
CONST
Depth = 2500;
TYPE
Month= (NoMonth , Jan , Feb, Mar, Apr, May, Jun,
Jul, Aug , Sep , Oct , Nov , Dec);
Date = RECORD
Mo: Month;
Day: 1 .. 31;
Year: I NTEGER
END;
PRecord = RE CORD
• • • I
END;
EltType = PRecord;
Not every possible change can be made in this way. For example, the array subscripts canna-
be changed to CHAR values by setting
Depth = 'Z';
because Depth is used in ranges beginning with an INTEGER constant, and the operations use
INTEGER operations to manipulate the subscripts. Nor can a stack of files be created by
EltType = TEXT;
because assignments are used to move the Stack elements, and files may not be -assigned. It is
some consolation that an attempt to make these changes would result in syntactically incorrec
Pascal programs, so at least the programmer could not be misled into believing the change was
accomplished when it was not. When changes must be made in information that is distributed, it is
all too common to think a change is accomplished, and receive no syntactic clues that something has
been missed.
The Stack representation as an array and a pointer is fundamental to the module
implementation, yet it could require changing. Suppose that in an application where use of computer
memory is very expensive, stacks are required to grow large and shrink again. It might be discovered
that allocating an array large enough for the maximum size is too wasteful. Instead, the
representation should be a singly linked list of the stacked values in order from top to bottom.
Then, for example, a Stack of CHAR with B on the top and X on the bottom would appear as:
=rHr "J' X
rc
and pushing C would alter the structure to:
B .... "J' X
The text of a complete program utilizing Stack appears below. The module applicat ion Rever se
is unchanged bec a use the module hides the form of Stack, but the module implementat ion itself
had t o be completely rewritten.
23.1.1 Exercises
23.1.1 Describe in detail the changes that would be required to use the Stack module of Section
19.2.2 for a Stack of
a) REAL values.
b) Month values.
c) SET OF CHAR values.
d) TEXT values.
e) Stack of CHAR values.
Preview
Procedures and functions can be passed as parameters. In order to indicate that a formal
parameter is to be a procedure or a function, the name of the formal is prefixed with either the
reserved word PROCEDURE or FUNCTION. The number and type for each parameter and the result
type of a function parameter must be specified. The procedure Invoke shown below has a
procedure parameter ProcQ and a function parameter ProcR that itself has no parameters but
returns an INTEGER . The body of Invoke contains procedure statements that actually call the
BEGIN
Invoke(Oneint, FuncF);
END
then Oneint and FuncF are not called as a part of the call on Invoke. Instead, these routines
are made available so that they are called when the statements within Invoke use the parameter
names. The procedure statement ProcQ (IVal) in Invoke actually calls Oneint and the
function call ProcR actually calls FuncF.
PROGRAM Bisect(OUTPUT);
CONST
Epsilon = lE-5;
VAR
Z: REAL;
Cnt: INTEGER;
PROCEDURE FindZero(FUNCTION Func(X: REAL): REAL;
Low, High: REAL;
VAR Zero: REAL; VAR It: INTEGER);
{Find a value for Zero such that
Low<=Zero<=High and monotonic Func(X)=O
for an X within Epsilon of Zero. The
number of bisections is returned in It.}
LABEL
4;
BEGIN {FindZero}
It := 0;
Zero := Low; {in case loop body is not executed}
If Func(Low) * Func(High) > 0.0
THEN {no zero crossing for monotonic Func}
BEGIN
WRITELN('Bad endpoints supplied to FindZero');
GOTO 4
END;
{Otherwise, proceed with binary search}
WHILE ABS(High - Low) > Epsilon
DO
BEGIN
It := It + 1;
Zero := (High + Low) I 2;
IF Func(Zero) < 0
THEN
IF Func(Low) < 0
THEN
Low := Zero
ELSE
High := Zero
ELSE
IF Func(High) >= 0.0
THEN
High := Zero
ELSE
Low := Zero
END ;
4:
END; {FindZero}
FUNCTION Plusl(RArg: REAL): REAL;
BEGIN {Plusl}
Plusl := RArg + 1.0
END; {Plusl}
- ·- --~ . -
. '
BEGIN {Bisect}
FindZero(Plus1, -2.0, 2.0, Z, Cnt);
WRITELN(Z:12:8, Epsilon:10:6, Cnt:4)
END. {Bisect}
Execution
OUTPUT: -1.00000763 0.000010 19
The program required 19 iterations to find the zero to within 0.00001.
~~~ ··
CONST
NumElts = 5;
EltLen = 10;
TYPE
EltType =PACKED ARRAY [l .. EltLen] OF CHAR;
FUNCTION Compare(VAR X, Y: EltType): BOOLEAN;
BEGIN {Compare}
Compare := X <= Y
END; {Compare}
PROCEDURE Exchange(VAR X, Y: EltType);
VAR
Temp: EltType;
BEGIN {Exchange}
Temp := X;
X := Y;
Y : = Temp
END ; {Exchange}
T he driver program below reads a nd writes one string of chara cters per line, storing them in
the array ToBeSorted.
PROGRAM SortStrings(INPUT, OUTPUT);
{Include string type declarations and operations}
VAR
ToBeSorted: EltArray;
TempElt: ARRAY [l .. EltLen] OF EltType;
I, J: 0 . . NumElts;
{Include PROCEDURE Sort(VAR Va l : El tArray;
NumToSort: INTEGER;
FUNCTION Orde red(VAR A, B: EltType) : BOOLEAN;
PROCEDURE Swap (VAR X, Y: EltType))
Permute the elements Val[l], .. . , Val[NumTo Sort]
s o that f o r al l i,
O<= i<NumToS or t <=NumEl ts : Va l[i ]<=Va l[i +1 ]}
BEGI N {SortStr i ngs }
WRI TELN('The input:') ;
{re a d and e cho inpu t}
I := 0 ;
WHI LE (I < NumElts) AND NOT EOF
DO
BEGI N
I : = I + 1;
J := 0;
WHILE (J < EltLen) AND NOT EOLN
DO
BEGIN
J := J + 1;
READ(TempElt[J]);
WRITE(TempElt[J] )
END;
READLN;
WRITELN;
{blank fil l the strings}
FOR J := J+l TO EltLen
DO
23.2.6 Exercises
23.2.1 Given the syntactically correct procedure statement
CallitAndPassit(Fun(X))
but no access to the declarations of the identifiers, give as much information as is certain about the
parameter of CallitAndPassit.
Preview
Information cannot always be divided into one set of fixed fields to form a Pascal record. When
different formats could be conveniently mixed, the variant record provides a type to encompass
them all.
A record binds together several different types as its fields. The record types presented
beginning in Chapter 16 have each had a fixed list of types; that is, each value had the same fields.
Real information is often not so simple. For example, in a collection of personnel records (name,
address, social security number, etc.) there will be some fields that are appropriate for only some
employees. Not all people will be on yearly salaries; instead some records will need fields for hourly
CASE P.WageType Of
Hourly: WRITELN (P.Hours *' P.HourlyRate);
Salary: WRITELN (P .YearlyRate DIV 52)
END;
END
The CASE statement selects s t a t ements in which the fields match the similar part of the CASE
construction in the record declaration. Care must be taken to set the correct tag value in any value
of type Person. The tag is just like other fields; it has no initial value and does not automatically
adjust when the variant fields are changed. Strange results can be obtained by inconsistently
manipulating the tag and the variant fields , as in:
P.WageType := Hourly;
P.YearlyRate := 35000; {P.WageType is still Hourly}
CASE P.WageType Of {execute the Hourly case
with unpredictable results}
Hourly: WRITELN(P.Hours * P.HourlyRate);
Salary: WRITELN(P.YearlyRate DIV 52)
END; I
The variant part of a record need not have a tag at all- then it is said to b~ free. The fields of
a free variant are expected to occupy the same storage space in a particular Pascal machine, and
provide a way to circumvent the type restrictions of the language. For example, if a programmer
knows that a CHAR variable and an INTEGER variable occupy the same space, then the INTEGER
value of A can be printed as follows:
PROGRAM Convert(OUTPUT);
TYPE
CvtType = (Int, Chr);
Cvt = RECORD
CASE CvtType Of
Int: (IVal: INTEGER) ;
Chr: (CVal: CHAR)
END;
VAR
Both: Cvt;
BEGIN
Both.CVal :='A';
WRITELN (Both.IVal)
END.
Execution
OUTPUT: 65
Variant records with free variants are a feature of Pascal that should be avoided, since they cannot
be expected to work in the same way on different Pascal machines.
. -
' '
' .~ • • t
Design Part 1.1
{read words from Common and insert in HashT}
RESET(Common);
WHILE NOT EOF(Common)
DO
BEGIN
ReadWord(Common,Temp,Found);
IF Found
THEN
Hashinsert(HashT,Temp)
END;
ReadWord can also be used to obtain words from INPUT. HashProbe is a function that returns
TRUE if a word occurs in a hash table, and FALSE if it does not.
Design Par~ 1.2
{read a word from INPUT; a word not in Common
or WordList should be added to WordList with
l occurrence, while a word not in Common but
already in WordList should have an extra
occurrence recorded}
WHILE NOT EOF
DO
BEGIN
ReadWord(INPUT,Temp,Found);
IF Found AND NOT HashProbe(HashT,Temp)
THEN
Listinsert(WordList,Temp)
END
The declarations for HashT and WordList can be given now that the processing algorithm
has been fixed. WordList is a list of nodes, each containing a word and an integer. HashT is
essentially an array of word lists, so the same procedures can be used to maintain it that maintain
WordList, by making them variants of the same RECORD type . Although each element of a word
list needs an integer (Occurs) to count how often the word appears, in a hash table element this
component is missing.
Design Part 1.3
{Declare:
Common - blocked-word file
HashT - internal table of blocked words
WordList - concordance of nonblocked words}
CONST
Max = 20; {Longest word}
TSize = 17; {Size of the hash table}
TYPE
WordType =PACKED ARRAY[l .. Max] OF CHAR;
NodeType = (HTable, WList);
List = tNode;
Node = RECORD
Next: List;
Word: WordType;
CASE Tag: NodeType OF
HTable: ();
WList: (Occurs: O .. MAXINT)
END;
~ --
.,, '--
-
The design of the insertion routine is a challenge-how much common processing can be done
for word lists and hash buckets? Both are free of duplicate entries, but a word-list element has the
extra Occurs component to process. If the word to be be inserted is already on the list, Insert"
returns a pointer to it; otherwise, a new list member is created and a pointer to it returned
Found can be used to distinguish these cases.
Design Part 1.2.1
PROCEDURE Insert(VAR L, Elt: List;
VAR Txt: WordType;
VAR Found:BOOLEAN);
{L is a list of n records, one of whose fields,
Word, is a WordType value.
(If Txt has the same value as the Word field of
the node at position j -->
Elt,Found := j,TRUE)
(If Txt does not occur as any Word field in L -->
L,Elt,Found :=
L with Txt appended in position n+l,
n+l, FALSE) }
VAR
Prev, Curr: List;
BEGIN {Insert}
{(Txt matches the Word field of the element
at position j in L -->
Found,Prev,Curr :=
TRUE, address of L ' s element whose Next
field contains the address of L's j'th
element, address of L's j'th element) 1
(Txt matches none of the Word fields of L -->
Found,Prev,Curr :=
FALSE, address of L's last element, NIL)};
IF Found
THEN
Elt := Curr
ELSE
BEGIN
NEW(Elt);
Eltf.Word := Txt;
{insert Elt after Prevf}
END
END; {Insert}
.At!. in Section 20.2.3, two pointer variables Prev and Curr are used to search for a word. If the
search is unsuccessful, Prev is made to point to the last element in the list, where an insertion may
be made.
23.3.4 Exercises
23.3.1 In the Person example of Section 23.3, why would two different procedures need to be
written to process the t ypes HPe r s on and SPer son?
23.3.2 A procedure Draw is to be written t o handle objects representing points, lines, and circles.
Assume that a point is represented by a pa ir of coordinates (two INTEGER values), a line by two
points, and a circle by a point a nd an INTEGER r adius.
a) Give the type declarations needed t o permit Draw to use a single parameter whose value
might be any of these types.
b) Give a skeleton of t he code within Draw t hat would use the parameter to choose which part of
the code to execute . Suggest some parts of the processing in Draw that would benefit from
having just one routine for all three objects.
23.3.3 A programmer claims he can print the INTEGER representation of the BOOLEAN value
TRUE with the following program fragment:
new(Z);
Zf.Bool :=TRUE;
WRITELN (Zf . Int)
If this can be done, give the type definitions that make this fragment work . Otherwise, explain why
this is not possible .
23.3.4 Write a procedure GetToken that scans a TEXT file and returns a series of tokens (words,
integers, punctuation marks, and an end token). A token is represented as a variant record; the
punctuation and end token have empty variant fields.
PROGRAM Scan(INPUT,OUTPUT);
{break input into a series of tokens}
CONST
MaxWordLength = 20;
TYPE
Word= PACKED ARRAY [l . . MaxWordLength] OF CHAR;
TokenType = (Txt, Int, EoT, Period, QMark,
XMark, Comma, Colon, Semicolon,
Undefined);
Token RECORD
CASE T: TokenType OF
Txt: (CValue: Word);
Int: (!Value: INTEGER) ;
EaT, Period, Comma, QMark, XMark,
Colon, Semicolon, Undefined: ()
END;
PROCEDURE GetToken(VAR Symbol : Token);
{Fill in the code};
Chapter Preview
The solution to a final problem illustrates the design and analysis methods of this text. By using
analysis within the design, confidence in the program is maintained as it grows. The design
method scales up to problems of arbitrary size by decomposing large problems into a hierarchy
of smaller ones, and maintaining intellectual control of the components. Complete formality
cannot be sustained for a large problem, but formal methods can be selectively ap'plied to
problems of any size.
This book has introduced two major design strategies that complement and build on one another.
The first is stepwise refinement of Pascal program parts, particularly Pascal statements; at each step
a relational or functional specification is refined into a small design and further subspecifications.
The second is designing modules with data abstractions to gain intellectual control. When carefully
chosen, such modules can be reused as building blocks for other designs.
This chapter contains an integrated example that illustrates both stepwise refinement and data
abstraction. The example, to find and print a set of prime numbers, is large enough to require the
judicious use of formal proofs, embedded in a broader activity of informal reasoning and design. In
particular, the example illustrates that stepwise refinement permits the control of details, step by
step, in both data and operations. For example, a subspecification need only treat variables that are
used in the design it supports and not the variables that are introduced for its implementation.
Deferring details while maintaining control of the design is essential in scaling up methods of
"programming in the small" to "programming in the large." Both stepwise refinement and data
abstraction are vital in scaling up. Stepwise refinement permits the deferring of details directly as a
program is elaborated a part at a time. Data abstraction permits the deferring of details indirectly,
but with even more power, by increasing the capability of the underlying design primitives.
Preview
The problem: to find and print the prime numbers up to a given integer.
~ ;- \
' '' . -
' .
rather than a set . As each new prime is found, it will be added to the list.
,
640 ONE LAST SOLUTION
2. (NextP > N -+ f) = (NextP > N -+ )
The definition of OddPr imes assures us that if NextP>N then
OddPrimes(NextP, N) = <>.
Thus I reduces to
(NextP>N and Odd(NextP) -+ Primes := Primes & <>)
which is an identity function with a smaller domain than the identity function on the right side of
step 2. Again, the even case is not of interest .
3.1=
( (NextP < N -+
Primes,NextP :=Primes & <NextP: Prime?(NextP)>,NextP+2)
(NextP > N -+ ) ) o I
The composition can be studied with a trace table:
Condition:
Primes= <P1, ... ,Pn> AND
<P1> & <P2, ... ,Pn> = <P1, ... ,Pi> & <Pi+1, ... ,Pn> AND
P1 = Pi and i >= 1
Picking i=1 simplifies the condition to Primes = <P 1, ... , Pn>.
Assignment to IsPrime:
TRUE AND (NextP mod P1 <> 0) AND
(for all Pj in <Pi+1, ... ,Pn>, NextP mod Pj <> 0}
which is
TRUE AND (NextP mod P1 <> 0) AND
(for all Pj in <P2, ... ,Pn>, NextP mod Pj <> 0)
or
(for all Pj in <P1, ... ,Pn>, NextP mod Pj <> 0)
Thus whether or not Primes is empty, the desired function is computed. Since this design part
meets its specification, the final comment can be refined .
Design Part 1.1.1.1.1.1
{(Primes= <P1, ... ,Pi> & <Pi+l, ... ,Pn> and Trial= Pi and i>=1 -->
IsPrime := IsPrime and (NextP mod Trial <> 0) and
(for all Pj in <Pi+1, ... ,Pn>, NextP mod Pj <> 0)}
(Trial= EndList --> )}
WHILE Trial <> EndList
DO
BEGIN
IsPrime := IsPrime AND (NextP mod Trial) <> 0;
Next(Primes, Trial)
END
To demonstrate that the WHILE statement computes the Design Part function, we make several
simplifying assumptions. The only output variable is IsPrime; changes to Trial and Primes
are ignored. We also discount the possibility of the result of the mod function being undefined in the
case where the second argument is not a positive integer. Since these arguments are chosen from
Primes whose members are all positive, the operation will always be defined.
The first two steps of the WHILE statement verification rule are straightforward.
Next (Primes, Trial) assigns the value of the next element in the list Primes to Trial so
eventually EndList must be returned. By assumption, the mod function is always defined. Thus
the loop will terminate on the same domain for which the function is defined. The loop and the
function both compute an identity function for the same domain. That is, when Trial=EndList,
Primes is the empty list.
Let g be the specified function and T name the following program text:
.
C ase 2
Condition IsPrime Trial Primes
Primes = IsPrime and EndList
<Pl, ... , Pn> & <> (NextP mod Trial ¥=- 0)
and Trial = Pn
and Trial ¥=- EndList
and n>=l
EndList = EndList
Condition:
Primes= <Pl, ... ,Pn> & <>and Trial=Pn and Trial¥=-EndList and n>=l
Trial=Pn implies Tr ial¥=-EndList so the latter condition can be omitted.
Assignrnent to IsPrime:
IsPrime and (NextP mod Trial ¥=- 0)
Thus the conditional assignment is
Primes= <Pl, ... ,Pn> & <>and Trial= Pn and n > 1 -
IsPrime :=
IsPrime and (NextP mod Trial ¥=- 0) and
(for all Pj in <Pn+l, ... ,Pn>, NextP mod Pj <> 0)
since the last conjunct is vacuously true. The remaining compositions of the third part of[!] with g
result in an empty function and an identity function with domain guard Tr ial=EndList.
The function resulting from this composition is:
(Primes= <P1, ... ,Pi-1> & <Pi, .. . , Pn> and Trial= Pi-1 and i > 2 -
IsPrime :=
IsPrime and (NextP mod Trial ¥=- 0) and
( Pj in <Pi, ... ,Pn>, NextP mod Pj ¥=- 0) )
(Primes= <Pl, ... ,Pn> & <>and Trial= Pn and n>1-
IsPrime :=
IsPrime and (NextP mod Trial ¥=- 0) and
( Pj in <Pi+l, . .. ,Pn>, NextP mod Pj ¥=- 0) }
(Trial = EndList - )
To convince ourselves that this function is the same as g, we observe the similarities between the
guards of the first two parts of this function.
1. P r imes is a list of integers separated into t wo parts, the first of wh ich contains at least one
eleme nt.
2. The value of Trial is the same as that of the last element in the first part of Primes, and
that value differs from EndList .
Thus the second part of the function is just a special case of the first, and the two can be combined
AList o Headabs
Condition L Result
O<L.C<L.S< MaxSize <L.V[l], ... ,L.V[L.C]> &
<L . VrL.C+11 ... L.VrL.Sl>
<L. V [1], . .. , L. V [L. C] > & EndList
<L.V[L.C+l], ... ,L.V[L.S]>
= <>
The condition evaluates to TRUE when
<L.V[l], ... ,L.V[L.C]> =<>AND
<L.V[L.C+1], ... ,L.V[L.SJ> = <>
Picking L . S and L. C to be 0 achieves this result. Thus the function is
MaxSize>=O AND L . S=O AND L.C=O -+ L,Result := <>,EndList
AList 0 Headabs
Condition L Result
o<L.C<L.S<
- - - MaxSize
<L.V[1], ... ,L.V[L.CJ> &
<L.V[L.C+1], . . . ,L.V[L.S]>
=
<L1, ... ,Li-1> & <Li, .. . ,Ln> <L1> & <L2, .. . ,Ln> L1
This condition is TRUE when i>1 (i.e., L. s>L. c>1) and L. V [1] =L1, • • • I L. V [L. SJ =Ln.
Thus the function is
Headcon o AList
Condition L L.C Result
L.S<=O EndList
o<L. c<L. s< MaxSize <L.V[l], ... ,L.V[L.C]> &
<L.V[L.C+1], ... ,L.V[L.S]>
Since the condition requires L. S<=O and L. S>=O, it must be the case that L. S=O, and hence
L. C=O, so the list
<L.V[1], ... ,L.V[L.C]> & <L.V[L.C+1], ... ,L.V[L.S]>
must be empty. Thus the function is
MaxSize>=O AND L.S=O AND L.C=O -+ L,Result := <>,EndList
The composition Headcon o AList yields
(MaxSize>=L.S>=1 -+
L,Resu1t := <L.V[l]> & <L.V[2], .. . ,L.V[L.S]>, L.V[1])
(MaxSize>=O AND L.S=O AND L.C=O -+ L,Result := <>,EndList)
This composition yields the same results as the previous composition, but on a slightly larger domain
since it places no restriction on the initial value of L. C.
Append maps a List and an El tType value to a List value.
PROCEDURE Append(VAR L: List; Elt: EltType);
{abs: (Length(L) < MaxSize --> L := L & <Elt>)
(Length(L) >= MaxSize --> )
con: (L.Size < MaxSize --> L.Values, L.Size :=
alpha(L . Values,L.Size+1,Elt), L.Size + 1)
(L.Size >= MaxSize --> ) }
BEGIN {Append}
IF L.Size < MaxSize
THEN
BEGIN
L.Size := L.Size + 1;
L.Values[L.Size] := Elt
END
END; {Append}
To test the data abstraction properly so that it can be reused, we should at least check the possible
combinations of operations listed above on three representative list values: an empty list, a partially
full list, and a full list. To make testing easier, the maximum list size is temporarily restricted to
four elements.
••
.. ..
:-·•, i ~ '
• •
PROGRAM TestList(OUTPUT);
CONST
MaxSize = 4; {short lists for now}
EndList = 0;
TYPE
EltType = O .. MAXINT;
List = ...
VAR
L: List;
NextP: EltType;
Trial: EltType;
{include
PROCEDURE EmptyList(VAR L: List);
abs: L := <>
PROCEDURE Append(VAR L: List; Elt: EltType);
abs~ (Length(L) < MaxSize --> L := L & <Elt>)
(Length(L} >= MaxSize --> )
PROCEDURE Head(VAR L: List; VAR Result: INTEGER);
abs: (L = <> --> Result := EndList) 1
(L = <L1, ... ,Li-1> & <Li, ... ,Ln> -->
L, Result:= <L1> & <L2, ... ,Ln>, L1}
PROCEDURE Next(VAR L: List; VAR Result: INTEGER);
abs: (L = <L1, ... ,Ln> & <>-->Result:= EndList)
(L = <L1, ... ,Li-1> & <Li, ... ,Ln> -->
L, Result:= <L1, ... ,Li> & <Li+1, ... ,Ln>, Li)
PROCEDURE Test(VAR L: List); }
BEGIN {TestList}
EmptyList(L);
WRITELN('testing <>');
Test(L};
EmptyList(L);
Append(L,1); {case 2 EmptyList; Append}
Append(L,2); {case 6 Append; Append}
WRITELN('testing <1,2>');
Test·(L);
EmptyList(L);
Append(L,1};
Append(L,2);
Append(L,3);
WRITELN('testing <1,2,3>');
Test(L)
END. {TestList}
Procedure Test shown below covers most legal combinations of operations except those ending with
EmptyList. Note that there are two different methods used to write list elements: sequencing
through the list using Next and printing the array elements directly. We do this so that the list
elements can be displayed without changing the last operation executed .
.
PROCEDURE Test(VAR L: List);
VAR
Temp: EltType;
I: INTEGER;
BEGIN {Test}
WRITE( 1 enter test, 1= < 1 ) ;
FOR I := 1 to L.Size
DO
WRITE(L.Values[I]: 4)
WRITELN (I >I) ;
Head(L,Temp);
WRITELN( 1 (emptylist or append); head = 1 Temp);
Head(L,Temp);
WRITELN( 1 head; head = 1 , Temp);
WRITE( 1 full sequence: head; next**i, i>=O: < 1 ) ;
WHILE Temp <> EndList
DO
BEGIN
WRITE(Temp: 4);
Next(L,Temp)
END;
WRITELN (I >I) ;
Head(L,Temp);
WRITELN( 1 next**i; head = 1 Temp);
IF Temp <> EndList
THEN
BEGIN
Next(L,Temp);
WRITELN( 1 part sequence: head; next = 1 Temp)
END;
Head(L,Temp);
WRITELN( 1 head; (next or null);head -~ Temp);
WRITE( 1 head; append(lS) = < 1 ) ;
Append(L,l5);
FOR I := 1 to L.Size
DO {write the list elements without Head or Next}
WRITE(L.Values[I]: 4)
WRITELN( 1 > 1 ) ;
Head(L,Temp);
IF Temp <> EndList
THEN
BEGIN
WRITE( 1 next; append(25) = <');
Next(L,Temp);
Append(L,25);
FOR I : = 1 to L.Size
DO
WRITE(L.Values[I]: 4)
WRITELN(' >')
END ;
Head(L,Temp);
WRITELN('append; head=' Temp);
WRITE('full sequence: head; next**i, i>=O: <');
WHILE Temp <> EndList
Test Results
Output Case
testing <>
enter test, 1= < >
(emptylist or append); head= 0 3
head; head = 0 11
full sequence: head; next**i, i>=O: < >
next**i; head = 0 11
head; (next or null);head = 0 11
head; append (15) = < 15 > 10
next ; append(25) = < 15 25 > 14
append ; head = 15 7
full sequence : head; next ** i, i>=O: < 15 25 > 12,16
testing <1,2>
enter test, 1= < 1 2 >
(emptylist or append) ; head = 1 7
head; head = 1 11
full sequence: head ; next* * i, i >=O : < 1 2 > 12,16
next**i; head = 1 15
part sequence: head; next = 2 12
head; (next or null);head = 1 12,15
head; append (15) = < 1 2 15 > 10
next; append (25) = < 1 2 15 25 > 8,14
append; head = 1 7
full sequence: head; next** i, i>=O : < 1 2 15 25 > 12,16
testing <1,2,3>
enter test, 1= < 1 2 3 >
(emptylist or append); head = 1 7
head; head = 1 11
full sequence: head; next** i, i>=O: < 1 2 3 > 12,16
next**i; head = 1 15
part sequence: head; nex t = 2 12
head; (next pr null);head = 1 12,15
head; append (15) = < 1 2 3 15 > 10
next; append (25) = < 1 2 3 15 > 8,14
append; head = 1 7
full sequence: head; next* * i, i>=O: < 1 2 3 15 > 12,16
We act as test oracles judging that each Head or Next operation returns the correct result. We
also note that appending two elements to an empty list yields a 2-list, to a 3-list yields a 4-list (the
last Append is ignored), and t o a 4-list leaves t he list unchanged. Our test program fails to cover
sever al simple cases. Cases 2 and 6 are covered in t he body of the test program when the list objects
are constructed. C ases 1, 5, 9, and 13 all concern setting an object t o the EmptyList aft er ot her
,·
Syntax Rules
A string is a CF Pascal program (subject to the restrictions on standard words given in Appendix II
and the context rules given below) if and only it it can be obtained by reading the leaves from a
syntax tree rooted at <program> from left to right and inserting blanks and comments
appropriately . Tlie syntax trees for CF Pascal are constructed using the following rules.
SRl. <program> ::= <heading> ; <block> .
SR2. <heading>::= PROGRAM <identifier> (INPUT, OUTPUT)
SR3. <identifier> ::= <letter>
I <identifier> <letter>
I <identifier> <digit>
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
SR4 · <letter> ··-A .. - I B I C I DI E I F I G IH I I I J I KI L IM I NI 0 I P I Q I R I S I T I
1 1 1 1 1 1 1 1 1 1 1
U I V I W1I X I Y I Z I a I b I c I d I e I f I g 1I h 1I i 1I J' 1I k 1I 1 1I m 1I n I1
o 1I p 1I q 1I r 1I s 1I t 1I u 1I v 1I w 1I x 1I y 1I z
SR5. <digit> ::= 0 ll I 2 I 3 I 4 I 5 I 6 I 7 I 8 I 9
SR6. <block> ::= <BEGIN statement>
I <declarations> ; <BEGIN statement>
I <procedure list> ; <BEGIN statement>
I <declarations> ; <procedure list> ; <BEGIN statement>
SR7. <declarations> ::= VAR <identifier list> : <type>
I <declarations> ; <identifier list> : <type>
SR8. <identifier list> ::= <identifier>
I <identifier list> , <identifier>
SR9. <BEGIN statement> ::= BEGIN <statement list> END
SRIO. <statement list> ::= <statement>
I <statement list> ; <statement>
SRll. <statement> ::= <READ statement>
<WRITE statement>
<assignment statement>
<null statement>
<BEGIN statement>
<IF statement>
<WHILE statement>
<procedure statement>
SR12 . <READ statement> ::=READ ( <identifier list> )
I RESET ( <identifier> ) I READLN ( <identifier list> )
SR13. <WRITE statement> ::=WRITE ( <write list> )
I WRITELN ( <write list> )
IWRITELN
I REWRITE ( <identifier> )
SR14. <write list> ::=<write item>
I <write list> , <write item>
SR15. <write item>::= <identifier>
I 1 <character string> 1
SR16. <character string> ::= <character>
I <character string> <character >
Context Rules
In addition to the syntax rules, CF Pascal programs must satisfy the following context rules.
CRl. All identifiers in a declaration's identifier list must be unique.
CR2. No reserved word, e.g. IF, WHILE, etc. can be used as an identifier. (A list of the
reserved words in Pascal is given at the end of the next Section.)
CR3. Any <identifier> in a <statement> that is not a <procedure statement>, other than
standard identifiers (e.g., READ, WRITE, etc.) must appear in the <identifier list> of the
<declarations>.
CR4. The procedure identifier used in a <procedure statement> must appear in a
<procedure heading> of a <procedure> declaration. The <actual parameter list> of a
<procedure statement> must agree in length with the <formal parameter list> of the
associated <procedure heading>. The identifiers in the <formal parameter list> and the
<actual parameter list> must agree in type.
CRS. Variable and procedure names can't be the same <identifier>.
1.,..~ .... .
., ~
~.·
------
Reserved Words
Although the standard Pascal word PROGRAM appears to be an <identifier>, it may not be used as
one in forming Pascal programs. PROGRAM is a reserved word in Pascal, and its use is restricted to
the places it appears literally in a sy ntax rule (only in a <heading>). The reserved words of Pascal
are listed in the following t able. None of these words may be used as an <identifier> in CF Pascal
or in full Pascal.
Standard Identifiers
Some standard Pascal words (for example , INPUT, OUTPUT, WRITE) are not reserved, but
nevertheless should be avoided . They are standard identifiers that are by default attached to some
special purpose. The standard identifiers of Pascal are:
Index 8GS·
aggregate 373, 436 Fibonacci number 409
ordinal 373, 594 field 457
simple 373 tag 628
declaration 15 file data type 442, 521
array 518 operations 443
constant 415 meaning of declaration 169, 443
extent (§) 252 TEXT (see TEXT file)
meaning of V AR 168 finite state machine 352, 443
meaning of procedure 170, 325 floating point notation 593
meaning of file 443 FOR statement 471
scope 252, 461, 620 meaning 474
type 373, 417 analysis 477
deMorgan's laws 104 format directives 389, 595
dereference operation FORWARD declarations 433
(1 on pointer value) 560 function 157, 370
design part 68 domain 157
development program 68 composition (o) 166
DISPOSE (pointer operation) 560, 630 constant 157
distribu tivity 104 empty ( {}) 157
DIV (integer operator) 386-387 identity (I) 157
division (/) (real operator) 594 inverse 168, 403
DO Part rule 231 power notation (r 0 ) 216
domain of a function or relation 157 range 157
domain of an array 513 transpose (rT) 168
undefined 157
echoed input 21 value notation 157
encapsulation 344 zero of a function 620
enumerated data type 373 FUNCTION
meaning 375 declaration 423, 569
input/output 375 meaning 424
EOF 131, 390, 456 parameter 616
meaning 192, 444 predefined 426
EOLN 130, 456
meaning 192 garbage 563
equality symbol (=) GCD 492, 494, 583
(see relational operators) (greatest common divisor)
execution GET statement
aborted 20 meaning 444
conditional 23, 92 global references 251, 256, 619
endless 32 GOTO statement 573
iterative 30 and compound statements 579
normal20 guarded assignment(-+) 198
sequential 10, 163
symbolic 182 harmonic series 486, 570
state 164, 561 ha.sh
table 11 code 532
existence rule 231, 404 function 631
EXP (real function) 595 table 631
exponent 593 head of a string or list (8) 146, 149
exponentiation 595 Horner's rule 397, 608
host type 401
factorial 409
888 Index
---------
.,.
. '
. '
identifier 8 number
identity function 157, 199 base 261, 397
IF statement 25, 28 conversion 397
design rule 210 natural 261
meaning 195
implementation diagrams 501 observable values 609
IN (set membership operator) 436 ODD (integer function) 429
include comment 255 open statement 561
induction 321 operators
information hiding 344 associative 104, 381, 387
INTEGER data type 386 binary 100
constants 386 commutative 104
expression meaning 396 distribution 104
operators 386 infix 100
input/output 389 precedence 382, 387
inverse of function, relation 168, 403 prefix 99
unary 99
label 573 OR (Boolean operator) 101
lazy evaluation 379, 455 meaning 193
lexicographic order 254, 535 ORD (integer function) 430
line marker (/) 11, 130 overflow 392, 403, 492
linked structures 548, 564
list 148, 548, 564 PACK (array operation) 533
composition (V) 149 PAGE statement 448
concatenation (&) 149 parameters 241, 418, 420
empty ( < >) 149 actual 242
membership (E) 148 array 519
sublists 149 formal 242
LN (real function) 595 FUNCTION 616
local references 243, 249, 256 PROCEDURE 616
locator value (of pointer) 559 value 420, 461, 535
value parameter meaning 421
machine VAR 242,420
Pascal 7 V AR parameter meaning 245
state 342, 351 perfect results (real operation) 608
mantissa 593 period meaning 166
matrix 538 pointer data type 559
product 539 locator value 559
MAXINT 386 operations 560
membership in list, set (E) 148, 154 representing other types 569
MOD (integer operator) 387 target value 560
module 342, 344, 483 to variant records 630
precedes symbol ( <) 23
NEW (pointer operation) 560, 630 (see relational operators)
NIL (pointer constant) 560 PRED (ordinal function) 426
nonlocal references 251, 256, 619 preservation rule 188, 403
NOT (Boolean operator) 100 prime numbers 439
meaning 193 procedure declaration 78, 241
not ·equal symbol ( < >) meaning 170
(see relational operators) procedure parameter 616
null statement 27, 199 procedure statement 79, 241
meaning 175 meaning 179, 258, 325, 421
Index OM
recursive verification rule 326, 331 24, 25, 192-194, 594
program 7 relative errors (in real expressions) 608
analysis 404 REPEAT statement 480
correctness 335, 500 meaning 481
libraries 239, 255, 269 replication 372
meaning 142, 159 representation function 349, 484, 502
specification 239, 333, 339 representational errors
structured 560 (for real values) 607
unstructured 560 reserved word 51
program header 8 RESET statement 110, 448
meaning 165 meaning 177, 444
proof 337 REWRITE statement 110
by contradiction 234 ROUND (real function) 594
by induction 321 meaning 176, 443
PUT statement roundoff errors (in real expressions) 606
meaning 444
scaling factor 593
quasi-random number 599 scientific notation 593
question mark (?) 16 scope of identifiers 252, 461, 620
queue search 525
FIFO 354 binary 526
priority 370, 598 depth-first 559, .567
hash 532, 631
range of a function or relation 157 sequential 526
rational numbers 486, 570 sentinel 391, 526
READ statement 16, 19, 110, 447 set 153, 370
meaning 177 builder notation 154
READLN statement 131 constant 153, 436
meaning 177 data type 435
REAL data type 593 difference(-) 155, 436
constants 593 empty ( {}) 154
errors analysis expression meaning 438
input/output 595 intersection (nor*) 155, 436
operations 594 membership ( E or IN) 154, 436
RECORD data type 457, 483 power set 155, 436
meaning 459 singleton 154
variant part 627 subset (C or <=) 154, 436
recurrence equation 221, 326 superset(>=) 436
recursion 239, 308 union (U or +) 155, 436
in definition 47, 51, 382 side effects 425
mutual 433 simulation 597
tail 313 SIN (real function) 595
relation 156, 370 sort
composition (o) 166 binary tree 567
domain 157 bubble 133, 622
inverse 168 bucket 533
power notation (rn) 216 exchange 529
range 157 IfSort 92
transpose (rT) 168 insertion 532, 552, 566
relational operators key 549
merge 293, 530
(<>< =>==<> )
MinSort 96, 107
668 Index
recursive sorting 308, 313 list description 150
SelectSort 116, 292 state 151
SQR (integer, real function) 429, 595 trace table 182
SQRT (real function) 595 conditional 200
stack 370, 523, 614 transpose
standard description function, relation (rT) 168
(of abstract object) 485 tree
standard words 7 leaf 4, 557
state linked structure 548, 555, 567
abstract 484, 501 syntax 48
concrete 484, 501 trigonometric functions 595
execution 164, 561 TRUNC (real function) 594
state machine 342, 351 truth table 102
final state 352 truth values
start state 351 TYPE declaration 373, 417, 483, 613
transition 342, 352 type equality 417
stepwise refinement 65 assignment compatible 417, 421
stopping rule 188 compatible 417
string 142 same 417
composition ('V) 145
concatenation (&) 144 umon
decomposition 146 discriminated 628
empty (tt) 143 free 629
literal 8, 142 UNPACK (array operation) 534
substring() 145
structuring a program 581 variable
subrange data type 373, 400-402 buffer 442
subscript of an array variable 513 simple 15
meaning 518 subscripted 513
SUCC (ordinal function) 426 variant record 627
succeeds symbol(>) 24 Venn diagrams 155
(see relational operators)
syntax 7, 45 WHILE statement 30
literal 46 design rule 229, 404
metasymbol (::= < > I) 46, 47 iterative meaning 217
part 46 recursive meaning 214
rule 46 verification rule 220-222, 234-236
tree 48, 380 WITH statement 465
WRITE statement 10, 19, 110, 447
tag field 628 meaning 176
tail of a string or list (A) 146, 149 WRITELN statement 8, 19, 447
target value (of pointer) 560 meaning 176
termination
decidability 236
recursive procedures 328
WHILE statement 221, 231, 236
testing
boundary (or special) values 558
partition 279
structural 126, 279
TEXT files 110, 448
external 413
Index 66~