You are on page 1of 132

PASCAL

LECTURE NOTES

DR. ALEJANDRO DOMINGUEZ


ING. EDGAR HERRADOR

FUNDACION ARTURO ROSENBLUETH

1993
INTRODUCTION

Programming languages are divided in special purpose languages and


general purpose languages.

Each special purpose language is intended for a particular application


area.

Pascal is probably the best, widely available, general purpose


programming language in the world at the present time, especially as a
vehicle for learning the principles of good programming. Pascal was
named after the 17th century French mathematician, and it is available
on almost every computer - from great machines to tiny
microprocessors.

Pascal has three main advantages over most other languages. These
advantages are:

Transparency: A program is said to be transparent if it is easy to read


and easy understand. Given a well-written Pascal program, it can easily
be seen what its intended effect is.

Efficiency: This concerns the size of programs and the speed at which
they run on the computer. To be efficient, a program should be small
and fast - it does not matter how long the Pascal source program is.

Security: After writing a program in Pascal, the computer, and in


particular the compiler, is able to spot mistakes. Because Pascal
programs are transparent, the programmer tends to make fewer
mistakes. So, in this context, security does not mean locking something
away where no-one can get at it; it means that not much is likely to go
wrong without being noticed.

This course is to show how to write Pascal programs which are


transparent, efficient and secure.
CHAPTER 1

Getting Started

1.1. The form of a program

A Pascal program has the form

PROGRAM n (output)

BEGIN

{ statements (Pascal instructions) };

END.

The first line of the program is called the 'program heading'. The rest of
the program is the 'program body'.

PROGRAM, BEGIN and END are 'reserved words'. This means that they
can be used only in the context they are intended. They cannot serve as
names.

To distinguish reserved words from names, upper case (capitals) letters


are used for reserved words and (mainly) lower case letters for names.

The letter "n" above is a program name invented by the programmer.

A name in Pascal is called a identifier: It may contain only letters and


digits and must start with a letter.

Some compilers regard only the first eight characters of an identifier to be


significant and ignore the rest.

An identifier must be something meaningful.


The identifier "output" within the program heading signifies that the
program will produce output at the terminal. This is not a reserved word.

In Pascal, successive statements are separated by semicolons. Notice that


the program finishes with a full stop.

1.2 Output

1.2.1 'writeln'

A line of output can be produced by a 'writeln-statement' of the following


form

writeln('...')

where the dots represent the sequence of characters to be printed. The


apostrophes are called 'string quotes' and a sequence of characters
enclosed between string quotes is called a 'string'.

Thus, for example, the program

PROGRAM Merry1 (output);


BEGIN
writeln('Merry Christmas')
END.

will print the message

Merry Christmas
The writeln-statement may produce various outputs as it can be seen in the
following examples:

writeln('this''ll work won''t it') ---> this'll work won't it


writeln('Merry','Christmas') ---> MerryChristmas
writeln('Merry', 'Christmas') ---> MerryChristmas
writeln('Merry ', 'Christmas') ---> Merry Christmas

One writeln-statement produces all its outputs on one line. To produce


output on more than one line, more than one writeln-statement must be
used. The program

PROGRAM Merry2 (output);


BEGIN
writeln('Merry Christmas');
writeln('Happy New Year')
END.

will produce two lines of output

Merry Christmas
Happy New Year

The identifier "writeln" is the name of something known as a 'procedure'


and the strings supplied by the programmer are known as 'parameters'.

When a writeln-statement is activated one is said to be 'calling' the


procedure.

It is possible to call the procedure "writeln" with no parameters and then


its effect at run-time is to print nothing; it simply moves the cursor down
to the start of the next line.
The program

PROGRAM Merry3 (output);


BEGIN
writeln('Merry Christmas');
writeln;
writeln('Happy New Year')
END.

will produce three lines of output

Merry Christmas

Happy New Year

Thus in the above program the effect of the parameterless writeln-


statement is to produce a blank between "Merry Christmas" and "Happy
New Year".

1.2.2 'write'

A write-statement has the same form as a writeln-statement except that


"write" replaces "writeln" and at least one parameter must be supplied.

The effect at rub time is the same except that the current output line is not
terminated; output from successive write-statements appears on the same
line.
A writeln-statement with three parameters "a", "b" and "c"

writeln(a, b, c)

is equivalent to the two statements

write(a, b, c);
writeln

and these, in turn, are equivalent to the four statements

write(a);
write(b);
write(c);
writeln

1.3 Program layout

Certain layout conventions shall be adopted in what follows and the


programmers is advised to follow suit since they are a mayor contributory
factor towards transparency.

(a) Nothing precedes PROGRAM, BEGIN or END on a line.

(b) PROGRAM, BEGIN and END are aligned but information between
BEGIN and END (and, as it will be seen later, between PROGRAM and
BEGIN) is idented two or three spaces.

(c) A semicolon immediately follows the first of the two items it


separates.

(d) Two statements on the same line are separated by few spaces.

(e) A statement does not extend across one a line boundary (but this
constrain will be relaxed when statements get longer.
Because a semicolon separates one statement from the next, no semicolon
should follow a statement which precedes END. However, a semicolon at
this point is allowed and some people find that its inclusion simplifies the
task of inserting further statements at a later date.

In these lecture notes, no such superfluous semicolons will appear.

1.4 Comments

Pascal permits the inclusion of 'comments' within a program in order to


annotate it for the benefit of the human reader.

It is customary to insert a comment at the head of a program to describe


the function of the program and perhaps give such information as the
names of the authors and the date the program was last amended.

Any sequence of characters enclosed between braces, the curly brackets {


and }, is interpreted as a comment and has no effect upon the action of the
program.

A comment may also be set between the compound symbols (* and *). A
comment may appear at any point in the program at which a space would
be legal.

Here is an example of a program containing a comment.

PROGRAM Merry4 (output);

(* Prints Christmas and New Year messages *)

BEGIN
writeln('Merry Christmas');
writeln;
writeln('Happy New Year')
END.
CHAPTER 2

Think of a Number

2.1 Constants

A number is a particular example of a constant.

In Pascal numbers are of two types: 'integer' and 'real'.

Examples of integer constants (with no decimal point) are:

123 -1 0 4623784502 -7897 999

Examples of real constants (with a decimal point) are:

10.0 -347.8216 3.1415926

The largest integer which can be represented is available in the form of a


predefined constant named "maxint".

All integers must be in the range -maxint and +maxint.

A typical value of "maxint" is 32767 (2^15 - 1).

On a small computer, the reals range from -10^40 to +10^40


approximately and about seven significant figures might be recorded.

There are two ways of representing real numbers: 'fixed point' form, as
above, and 'floating point' form. The later has the form

aEb

number a can be an integer or fixed point real value


number b must be an integer
letter E is interpreted as "times 10 to the power"

Some examples are:


fixed point floating point

-347.8216 -3.478216E2
0.00001 1E-5
7200000000.0 7.2E9

2.2 Expressions

Numeric data can be processed using 'operators' and 'brackets'. The


operators are:

+ represents addition
- represents subtraction
* represents multiplication
/ represents (real) division

These operators are applied from left to right at any one bracket level
subject to the fact that

* and / take precedence over + and -

Here are some examples

5 + 2 * 3 = 11 (integer)
(5 + 2) * 3 = 21 (integer)
9.7 - 14.82 = -5.12 (real)
10 / 5= 2.0 (real)
7 + 8.4 / 2.1 * 2 = 15.0 (real)

Arithmetic expressions may be supplied as parameters to "writeln" but,


unlike strings, they are not enclosed within string quotes.
The program

PROGRAM Recta1(output);

(* For a rectangle of given dimensions,


this program prints its area *)

BEGIN
writeln('A rectangle of length 3 cm and width 2 cm');
writeln('has area ', 3 * 2, ' sq cm')
END.

produces the output

A rectangle of length 3 cm and width 2 cm


has area 6 sq cm

2.3 User-defined constants

Constant declarations appear between the PROGRAM line and the


BEGIN, heralded by a single occurrence of the reserved word CONST.

A constant 'declaration' has the form

CONST
i1 = c1;
i2 = c2;
... ...

where i1, i2, ... are identifiers


and c1, c2, ... are constants

Notice that each declaration is followed by a semicolon.


An example is the following program.

PROGRAM Recta2 (output);

(* For a rectangle of given dimensions,


this program prints its area *)

CONST
length = 3;
width = 2;

BEGIN
write('A rectangle of length ', length, ' cm');
writeln(' and width ', width, ' cm');
writeln(' has area ', length * width, ' sq cm')
END.

Apart from spacing and some minor modifications, this program produces
the same output as the previous version. But there are some
improvements:

'Transparency': The relevance of each constant, and hence each


expression, is clearer.

'Modification': This program can be applied to a different rectangles more


easily than the previous version - the values are changed in the constant
definition. In the previous program, the entire program has to be scanned
in order to change every occurrence of each constant. The improved
program is not only easier to modify, it is safer - there is less chance of
making a mistake when making changes.
2.4 Formatted output

A field width can be specified for any parameter of "write" or "writeln".

If the field is too wide, the output is 'right-justified' and the field 'space-
filled' from the left.

If the field width specified for an arithmetic value is too small, it is


ignored and the minimum field width necessary is used. So if a field width
of 1 is supplied, no additional spaces will be output.

If the parameter of "write" or "writeln" is a string or an integer number,


the syntax for a formatted output must be of the form

write(parameter : fieldwidth)
or writeln(parameter : filedwidth)

If the parameter of "write" or "writeln" is a real number, the syntax for a


formatted output must be of the form

write(parameter : fieldwidth : decimalplaces)


or writeln(parameter : fieldwidth : decimalplaces)

Thus, the three statements

writeln('*', 7:1, ' ' :6, 423 :1, 6 :5);


writeln('*', 49 :8, 'Fred' :9);
writeln('*', 5.6789 :5:2)

produce the following output

*7bbbbbb423bbbb6
*bbbbbb49bbbbbFred
*b5.6
2.5 Variables

A 'variable' is an identifier which may be associated with different values


(but all of the same type) at different times during the execution of a
program.

Variable declarations appear between the constant declarations (if there


are any) and BEGIN, heralded by the single occurrence of the reserved
word VAR.

Variable declarations have the form

VAR
a1, a2, a3, ..., an : type1;
b1, b2, b3, ..., bm : type2;
... ... ... ... ...

where a's and b's are identifiers


and type1 and type2 are 'types'

For the present types are restricted to the types "integer" and "real". These
are said to be 'scalar' types and "integer" types are also known as 'ordinal'
type.

Integer variables are used for things which can only be whole numbers.

Real variables are used for things which may be fractional numbers.

Notice that a variable declaration introduces a variable but associates no


value with it - its value is said to be 'undefined'.
2.6 Input

On way of giving a value to a variable is to type a value when the program


is running and make the program give this value to the variable. Then it is
said that the program 'reads' the value.

Supplying data at run-time has the advantage that different runs of one
program utilize different data; there is no need to change the program.

If a program is to read a value from the terminal, the identifier "input"


must appear in the PROGRAM line along with "output", the two being
separated by a comma.

PROGRAM ... (input, output);

In Pascal, reading can be achieved by either of two statements: "read" and


"readln".

2.6.1 'read'

A read-statement has the form

read(v1, v2, ..., vn)

where v1, v2, ..., vn are variables, possible of different types


The program

PROGRAM NexAge (input, output);


(* Increments a supplied age by 1 *)

VAR
agenow : integer;
BEGIN
write('How old are you? ');
readln(agenow);
writeln('After your next birthday you will be ', agenow+1)
END.

reads a person's age and states the age next birthday.

When this program is run nothing will happen until the user types an
integer. The program will then respond with the appropriate output.

2.6.2 'readln'

"readln" is to "read" as "writeln" is to "write". The form is the same except


that no parameter need be supplied.

When "readln" is supplied with no parameters

readln

the program stops until it is typed the return-key (enter-key).

When "readln" is supplied with parameters

readln(a, b, ..., z)

is equivalent to

read(a, b, ..., z) + skip line

The program
PROGRAM PetShop1 (input, output);

(* Sums four integers typed one per line,


each possibly followed by text *)

VAR
pet1, pet2, pet3, pet4 : integer;

BEGIN
writeln('Type the number in stock of each of your ',
'four species');
writeln(' - one per line');
readln(pet1);
readln(pet2);
readln(pet3);
readln(pet4);
writeln('Total number of pets in stock is ',
pet1 + pet2 + pet3 + pet4 : 1)
END.

accumulates the total stock in a pet shop which offers four different types
of pet. Each number is read from a different line and anything typed after
the number is ignored.

The data could be typed in the form

7 cats
4 dogs
2 birds
1 fish

and only the numbers would be read.


CHAPTER 3

Doing Arithmetic

3.1 Assignment statement

An expression can be used to assign a value to a variable. A value may be


assigned to a variable by an 'assignment statement'. This has the form

v := e

where v is a variable,
e is an expression
and := is the 'assignment operator'

Expression e may be a type integer if variable v has type real but


otherwise variable v and expression e have the same type.

Assignment operator := is read as 'becomes'.

When an assignment statement is obeyed the expression on the right hand


side is evaluated and the resultant value assigned to the variable on the
left.

Any value previously associated with the variable is lost ('overwritten').

A variable retains a value assigned to it until it subsequently acquires a


different value, either by reading or by direct assignment.

Expression e may include variables in the same way that in can include
constants.

When a variable is encountered within an expression at run-time the


current variable is used. Any reference to an undefined value constitutes
an error.

Return to program "PetShop1", that one can be modified as follows


PROGRAM PetShop2 (input, output);

(* Sums four integers typed one per line,


each possibly followed by text *)

VAR
pet1, pet2, pet3, pet4, totpets : integer;

BEGIN
writeln('Type the number in stock of each of your ',
'four species');
writeln(' - one per line');
readln(pet1);
readln(pet2);
readln(pet3);
readln(pet4);
totpets := pet1 + pet2 + pet3 + pet4;
writeln('Total number of pets in stock is ',
totpets :1)
END.

More than one assignment may be made to the same variable.

Also, a variable may appear on both the left hand side an the right hand
side of the same assignment statement.
Both of these points are illustrated in the following program which prints
a number and its three successors together with their squares and
reciprocals.

PROGRAM SqrtReci (input, output);

(* Prints four consecutive integers together


with their squares and reciprocals *)

VAR
n: integer;

BEGIN
write('What is the starting value? ');
readln(n);
writeln;
writeln(' 2');
writeln(' n n 1 / n');
writeln(n :4, n * n :6, 1 / n :8:3);
n := n+1;
writeln(n :4, n * n :6, 1 / n :8:3);
n := n+1;
writeln(n :4, n * n :6, 1 / n :8:3);
n := n+1;
writeln(n :4, n * n :6, 1 / n :8:3)
END.
3.2 Arithmetic expressions

3.2.1 'Arithmetic operators'

The two operators +, -, * and / are applicable to integer and real operands.

There are two operators which are applicable only to two integer operands
and each produces an integer result.

DIV - integer division (gives the integer part of division).

MOD - modulo reduction (gives the remainder left by


integer division).

DIV and MOD have the same precedence as * and /; i.e., {* / DIV MOD}
are applied before {+ -}.

The first operand should be positive or zero and the second should be
positive.

Here are some examples.

15 DIV 7 = 2 15 MOD 7 = 1

7 DIV 15 = 0 7 MOD 15 = 7

6 DIV 6 = 1 6 MOD 6 = 0
The following program takes a 3-digit integer, introduced as a user
defined constant, and prints it backwards.

PROGRAM RevInt (output);


(* Reverses a positive three digit integer,
specified as a user-defined constant *)

CONST
n = 472;

VAR
hundreds, tens, units : integer;

BEGIN
hundreds := n DIV 100;
tens := (n MOD 100) DIV 10;
units := n MOD 10;
writeln(n, ' reversed is ', units :1, tens :1, hundreds :1)
END.

3.2.2 'Standard functions'

There are six functions which are applicable to real and integer operands
'x' (called parameters) and each produces a real result.

sin(x) sine of x. x must be in radians


cos(x) cosine of x. x must be in radians
exp(x) e to the power x. e = base of natural logarithms
= 2.7182818284...
ln(x) natural logarithm of x. x must be > 0
sqrt(x) square root of x. x must be >= 0
arctan(x) arc tangent of x. Its result is in the range from
-pi to +pi radians

One way of computing the value of pi to the maximum accuracy of your


machine is knowing that tan(pi/4)=1, thus pi= 4 * arctan(1):

PROGRAM Pi (output);
(* Compute the value of pi to the maximum accuracy *)

BEGIN
write('According to the computer the value of pi is ');
writeln(4 * arctan(1) :1:15)
END.

Pascal does not provide an operation of exponentiation; i.e., the operation


of raising any positive number to any power. But this operation may be
constructed by an application of functions "exp(x)" and "ln".

The expression

exp(b * ln(a))

gives the value of "a" raised to the power "b".

There are two functions which can supply an integer or a real result,
depending upon the type of parameter supplied.

abs(x) absolute magnitude of x; i.e., x becomes


positive
sqr(x)square of x; i.e., x * x

The function "sqr(x)" should always be used when x is an expression more


complicated than a single constant or variable, since Pascal evaluates
functions more slowly than multiplications and additions.
As an example consider the following program

PROGRAM triang(input, output);

(* This program computes the length of the hypotenuse of a


right-angled triangle, given the lengths of the other two sides,
as well as the sine, cosine and tangent of each of the two
no-right-angles. It also computes the value of the
no-right-angles *)

VAR
h, s1, s2, a1, a2 : real;
sin1, sin2, cos1, cos2, tan1, tan2 : real;

BEGIN
writeln;
writeln('The right-angled triangle is:');
writeln;
writeln(' ¦ \ ');
writeln(' ¦a1\ ');
writeln('s1 ¦ \ h');
writeln(' ¦ \ ');
writeln(' ¦ a2 \ ');
writeln(' --------- ');
writeln(' s2 ');
writeln;
write('Enter length of side s1 = ');
readln(s1);
write('Enter length of side s2 = ');
readln(s2);
(* Start Computations *)

h := sqrt(s1 * s1 + s2 * s2); (* compute hypotenuse h *)


a1 := arctan(s2 / s1); (* compute angle a1 *)
sin1 := sin(a1); (* compute sine of a1 *)
cos1 := cos(a1); (* compute cosine of a1 *)
tan1 := sin1 / cos1; (* compute tangent of a1 *)
a2 := arctan(s1 / s2); (* compute angle a2 *)
sin2 := sin(a2); (* compute sine of a2 *)
cos2 := cos(a2); (* compute cosine of a2 *)
tan2 := sin2 / cos2; (* compute tangent of a2 *)

(* Write results *)

writeln;
writeln('The hypotenuse is = ',h :1:4);
writeln;
writeln('The angle a1 is = ',a1 :1:4, ' radians');
writeln('The sine of a1 is = ',sin1 :1:4);
writeln('The cosine of a1 is = ',cos1 :1:4);
writeln('The tangent of a1 is = ',tan1 :1:4);
writeln;
writeln('The angle a2 is = ',a2 :1:4, ' radians');
writeln('The sine of a2 is = ',sin2 :1:4);
writeln('The cosine of a2 is = ',cos2 :1:4);
writeln('The tangent of a2 is = ',tan2 :1:4)
END.
Two functions which map a real value onto an integer are

round(x) - rounds x to the nearest integer


trunc(x) - truncs x to a integer result

Here are some examples:

round(7.4) = 7 trunc(7.4) = 7
round(-7.4) = -7 trunc(-7.4) = -7
round(7.5) = 8 trunc(7.5) = 7
round(-7.5) = -8 trunc(-7.5) = -7
round(7.6) = 8 trunc(7.6) = 7
round(-7.6) = -8 trunc(-7.6) = -7

3.2.3 'Precision of reals'

Within the computer, integers are stored exactly but reals involve some
inherent error. This is because only certain number of significant figures
can be stored.

Suppose a decimal machine capable of storing four significant figures.


The expression 1/3 evaluated to four significant figures is 0.3333. The
expression 2 * (1 / 3) therefore produces 0.6666. The expression 2 / 3
evaluated to four significant figures is 0.6667. So two expressions which
are mathematically equivalent can produce different values.
CHAPTER 4

Being Choosy

4.1 If-statement

The if-statement exists in a full and a shortened form. The full form is
IF t THEN
s1
ELSE
s2

where t is a test
and s1 and s2 are any statements

Notice that no semicolons appear within the if-statement but a semicolon


will follow the if-statement (that is, after s2) if the if-statement is followed
by another statement.

When the statement is encountered at run-time the test t is made. If the test
holds then s1 is obeyed (and s2 is not); if the test does not hold then s2 is
obeyed (and s1 is not). I either case control then passes to the statement
next in sequence after the if-statement.

Rather than choose which of two statements to obey it is often wished to


make a simpler decision - whether to obey one statement or not.

An abbreviated form of the if-statement is available for this.

IF t THEN s

where t is a test
and s is any statement

If test t holds, s is obeyed; if not, s is not obeyed. In either case control


passes to the statement next in sequence after the if-statement.

Tests can include the relational operators


< less than
<= less than or equal to
> greater than
>= greater than or equal to
= equal to
<> not equal to

and these have the lowest precedence of all the operators.

The following program, which determines the smallest of three supplied


integers, includes both forms of if-statements.

PROGRAM minim3(input, output);

(* Determines the smallest of three integers *)

VAR
n1, n2, n3, min : integer;

BEGIN
write ('Type three integers: ');
readln(n1, n2, n3);

IF n1 < n2 THEN
min := n1
ELSE
min := n2;

IF n3 < min THEN


min := n3;

writeln('The smallest of these is = ', min :1)


END.

4.2 Case-statement
A case-statement provides a multi-way selection based on a range of
values. The usual form is

CASE e OF
v1 : s1;
v2 : s2;
...
vn ; sn
END (* CASE *)

where e is an expression of some ordinal type, say t,


v1, v2, ..., vn are distinct constants of type t
and s1, s2, ..., sn are statements

The only ordinal type it has been met is "integer" so, for the present, "t"
must be integer.

At run-time, only one of the 'limbs' s1, s2, ..., sn will be obeyed.

The 'selector' e is evaluated and, assuming it produces one of the values


v1, v2, ..., vn, the statement labeled with the appropriate value is selected.

The selector must evaluate to one of the supplied case labels; failure to do
so constitutes an error.

Use of a case-statement is appropriate when selection is


governed by an ordinal expression whose values are few and
known.
The following program uses a case statement to print the 'name' of a
supplied digit.

PROGRAM Digname1 (input, output);


(* Names a supplied digit *)

VAR
digit : integer;

BEGIN
write('Give me a digit ');
readln(digit);
write('This is ');
CASE digit OF
0 : writeln('zero');
1 : writeln('one');
2 : writeln('two');
3 : writeln('three');
4 : writeln('four');
5 : writeln('five');
6 : writeln('six');
7 : writeln('seven');
8 : writeln('eight');
9 : writeln('nine')
END (* CASE *);
END.

In the above program, it has been assumed that the cases listed are the
only possible ones.

Now assume that the number 23 is typed. This number is not a digit and,
when running the program, 23 in never encounter.

Pascal standard does not indicate what should happen if a value other than
those listed is encountered. For some compilers it will result in a run-time
error; for others, no statement is executed.
Some implementations of Pascal include, as a nonstandard extension, an
"else" case.

This is illustrated in the following program.

PROGRAM Digname2 (input, output);

(* Names a supplied digit *)

VAR
digit : integer;

BEGIN
write('Give me a digit ');
readln(digit);
write('This is ');
CASE digit OF
0 : writeln('zero');
1 : writeln('one');
2 : writeln('two');
3 : writeln('three');
4 : writeln('four');
5 : writeln('five');
6 : writeln('six');
7 : writeln('seven');
8 : writeln('eight');
9 : writeln('nine')
ELSE
writeln('no a digit')
END (* CASE *);
END.
One case limb may be prefixed by more than one case-label.

The next program illustrates this. This program converts number grades to
letter grades.

PROGRAM Grades (input, output);

(* Convert number grades to letter grades *)

VAR
grade : integer;

BEGIN
write('Enter the numeric grade: ');
readln(grade);
write('The letter grade is ');
CASE grade DIV 10 OF
9, 10 : writeln('A');
8 : writeln('B');
7 : writeln('C');
6 : writeln('D');
0..5 : writeln('F')
ELSE
writeln('no recorded')
END (* CASE *)
END.
4.3 Compound statement

Only one statement may be supplied as a case limb or following THEN or


ELSE but it often wished to supply more than one.

Pascal provides a means of bracketing statements together so that they


become one 'compound statement'.

BEGIN and END are used as statement brackets.

This is illustrated in the following program.

PROGRAM Order (input, output);

(* Order two given integer numbers


being the second the greatest *)

VAR
first, second, temp : integer;

BEGIN
write('Type two integer numbers: ');
readln(first, second);
IF first > second THEN
BEGIN
temp := first;
first := second;
second := temp;
END;
writeln('In order they are: ',first, ' ', second)
END.
4.4 Nested conditionals

A case limb or the statement following THEN or ELSE may itself be


conditional or contain a conditional statement.

The general form of a nested if-statement is the so-called "cascading" IF-


THEN-ELSE

IF ... THEN
...
ELSE
IF ... THEN
...
ELSE
...
IF ... THEN
...
ELSE
...
This form is illustrated in the following program

PROGRAM PosNeg (input, output);

(* Decides if a given real number is


either positive, negative or zero *)

VAR
n : real;

BEGIN
write('Enter a real number : ');
readln(n);
IF n > 0 THEN
writeln('The number is positive')
ELSE
IF n < 0 THEN
writeln('The number is negative')
ELSE
writeln('The number is zero')
END.

A nesting of the form

IF t1 THEN
IF t2 THEN
s1
ELSE
s2

is not recommended since it is less easy to follow.

When IF immediately follows THEN it can be difficult (for the human


reader) to sort out which ELSE goes with which THEN. This is
particularly so when one of the if-statements has no else-part.
For the Pascal compiler the ELSE will be associated with the second
THEN. However, this may not be apparent for the human reader. This is
called the "trailing problem".

There are two ways to improve transparence in this context.

One, often the best, is to rewrite this section of program so that the
problem does not arise.

Two, is to use statement brackets to explicitly associate the ELSE with its
intended THEN. BEGIN and END may be used to enclose a single
statement. The earlier schema can be transformed into either

IF t1 THEN
BEGIN
IF t2 THEN
s1
END
ELSE
s2

to associate the ELSE with the first THEN or

IF t1 THEN
BEGIN
IF t2 THEN
s1
ELSE
s2
END

to associate the ELSE with the second THEN.


On the other hand, a case-statement can be nested within an if-statement:

IF ... THEN
CASE ... OF
...
END (* CASE *)
ELSE
...
CHAPTER 5

Character by Character

5.1 Char type

By characters it is meant the letters of the alphabet (uppercase and


lowercase), the digits 0-9, and other special characters that are used for
punctuation, abbreviation, and so on.

A third scalar type is "char". It comprises all the characters available in the
set of characters used by the computer.

The most common sets are ASCII (American Standard Code for
Information Interchange) and EBCDIC (Extended Binary Coded Decimal
Interchange Code).

ASCII set will be assumed now on.

As with the real and integer types, it can be defined constant and variables
of type "char". Here are some character constant definitions:

CONST
space = ' ';
dot = '.';
zero = '0';

A character constant is a string of length 1.

A variable of type "char" is declared as:

VAR
x : char;

Variable x can take as its value any character in the set available.
When a value is read into a character variable only one character (string of
length 1) is read from the input stream.

When a character is output, unless a field width greater than 1 is specified,


no extra spaces will be printed; the character will occupy only one
character position.

This is illustrated in the next program.

PROGRAM Reveword (input, output);

(* Reverses any four letter word *)

VAR
c1, c2, c3, c4 : char;

BEGIN
write('Please type a four-letter word: ');
readln(c1, c2, c3, c4);
writeln('Reversed, this is: ', c4, c3, c2, c1)
END.

The ASCII set form an ordered sequence - this is called the 'lexicographic'
order:

Associated with each character is an 'ordinal number', its position in the


lexicographical order.

The first character has ordinal number 0, the second one ordinal number 1,
and so on.

Any data type whose values form a countable, ordered sequence is called
an 'ordinal' type.

That is why "integer" was described as an ordinal type (but "real" was
not). Thus characters can be used as case-statement selectors:
PROGRAM GiveName (input, output);
(* Prints one of four names given the initial *)
VAR
initial : char;

BEGIN
write ('What is your initial? : ');
readln(initial);
write('Hi ');
CASE initial OF
'B' : writeln('Brian');
'A' : writeln('Amanda');
'F' : writeln('Frank');
'C' : writeln('Christine')
END (* CASE *)
END.

Since the ASCII set is an ordered sequence, the relational operators can be
applied to any to operands of (the same) ordinal type.

Consequently, it can be assumed that relations such as

'A' < 'Z'


'D' > 'C'
'7' >= '4'
'9' < 'A'

are true.

Be clear in mind of the difference between the characters '0', '1', ..., '9' and
the integers 0, 1, ..., 9.

Characters can be assigned to character variables, but integers cannot be


assigned to characters variables. On the other hand, two integers can be
multiplied, two characters cannot be multiplied.

5.2 Standard functions


Since there is an order established for the characters, each character
except the first has a predecessor, and each one except the last has a
successor.

Predecessors and successors are delivered by two functions:

pred(x) - predecessor of char variable x


succ(x) - successor of char variable x

For example

pred('c') = 'b' succ('c') = 'd'

The following program reads a character and prints five consecutive


characters centred on the supplied character.

PROGRAM Charact5 (input, output);

(* Prints five consecutive characters centred on a


supplied character *)

VAR
ch : char;

BEGIN
write('Give me a character: ');
readln(ch);
writeln('Five characters centred on ', ch, ' are ',
pred(pred(ch)), pred(ch), ch,
succ(ch), succ(succ(ch)) )
END.
Two functions map between characters and their ordinal numbers. These
ones are

chr(x) - delivers a character given its ordinal


number x
ord(x) - delivers the ordinal number of a supplied
character x

Thus, the position in the alphabet of a lower case letter represented by a


variable "xx" is given by

ord(xx) - ord('a') + 1

and the letter at position "yy" in the alphabet (if "yy" is an integer variable
with a value in the range 1 to 26 is given by

chr(ord('a') + n -1)

The functions "ord(x)", "succ", and "pred" can be applied to items of any
ordinal type.

5.2 Text files

A file composed of characters and end-of-line markers is called a 'text'


file.

The two identifiers "input" and "output", used in the program heading, are
the names of the 'standard' text files, one to be used for reading and one
for writing.

In chapter 13 the whole aspect of files in detail shall be examined in


detail. For the moment it will be seen how a Pascal program can use
external, non-standard, text files not associated with the terminal.

There are four aspects of these files:


1. All external files with which the program is to communicate must be
named in the program heading.
2. Non-standard text files must be declared within the program as
variables type "text".

3. Before a non-standard file can be used it must be initialized either for


reading or for writing. A file "f" is initialized for reading by one of the
following procedures (depending on the Pascal implementation)

STANDARD PASCAL TURBO PASCAL

reset(f) assign(f, 'filename.$$$');


reset(f)

In both cases the procedure "reset" can be applied to the file several times
during the execution of a program and, each time, the file concerned is
initialized for reading from the beginning.

A file "g" is initialized for writing by one of the following procedures


(depending on the Pascal implementation)

STANDARD PASCAL TURBO PASCAL

rewrite(g) assign(g, 'filename.$$$');


rewrite(g)

In both implementations, file "f" must already exist (because it cannot


otherwise be read).

The file "g" need no to exist before the program is run. If it does exist, it
will be overwritten; if it does not, and empty file "g" will be automatically
created when the program is about to be run.

In Turbo Pascal a file must be closed after reading from or writing to it


using the statement "close(f)"

4. In both implementations, to read from or to write to a non-standard file,


files "f" or "g" must be supplied as the first parameter to the input/output
procedure.
For example, two characters would be read from a text file "f" by a
statement of the form

read(f, c1, c2) or readln(f, c1, c2)

and two characters would be written to a text file "g" by a statement of the
form

write(f, c1, c2) or writeln(f, c1, c2)

An example of the above four aspects is the following program, which is a


modification of program Reveword of section 5.1.

This new program reads the four-letter word from a file, say
"inrever4.$$$", and produces the reversed copy in another file, say
"outreve4.$$$".
PROGRAM Reverse4 (infile, outfile);

(* Reverses any four letter word *)

VAR
infile, outfile : text;
c1, c2, c3, c4 : char;

BEGIN

(* Read from file inrever4.$$$ *)

assign(infile, 'inrever4.$$$');
reset(infile);
readln(infile, c1, c2, c3, c4);
close(infile);

(* write to file outreve4.$$$ *)

assign(outfile, 'outreve4.$$$');
rewrite(outfile);
writeln(outfile, 'Reversed word: ', c4, c3, c2, c1);
close(outfile)
END.
CHAPTER 6

Round and Round Until You Are Finished

6.1 Loops

Computers are often wanted to repeat some process several times. When a
program obeys a group of statements several times, it is said to be
'looping'.

Loops are of two types: If it is known how many times it is wished to


repeat the loop the situation is said to be 'deterministic'; if not, it is 'non-
deterministic'.

6.2 'For-statement'

A deterministic loop is implemented by a 'for-statement'.

There are two forms of this statement: incremental (forward) for-statement


and decremental (backward) for-statement.

The general form of an incremental for-statement is

FOR v := e1 TO e2 DO
s

where v is a variable of some ordinal type, say t, e1 and e2


are expressions of type t
and s is any statement.
Some remarks must be made:

1. Expressions e1 and e2 are the minimum and maximum values of


variable v, respectively.

2. At the moment of starting the for-statement, variable v is initialised to


the value of expression e1 and its value compared with the value of
expression e2. In general, at the end of each loop variable v takes
successive values

e1, succ(e1), succ(succ(e1)), ..., e2

and its new value is compared with the value of e2. Thus, in some sense, v
is a control variable of the number of loops or repetitions.

3. Variable v is updated automatically and the programmer must make no


attempt to change its value within the loop.

4. Upon termination of the loop, variable has no value at all.

5. If e2 < e1, the loop body s is not obeyed at all; otherwise the body is
executed a number of times equal to

ord(e2) - ord(e1) + 1

Each execution of the loop is called an 'iteration'.

The general form of a decremental for-statement is

FOR v := e1 DOWNTO e2 DO
s

where v is a variable of some ordinal type, ast t, e1 and e2


are expression of type t
and s is any statement.
It might be noted here that 'DOWNTO' is one word, not two, there is no
space in it.

If e1 < e2 the loop body s is not obeyed at all; otherwise the loop body is
obeyed a number of times equal to

ord(e1) - ord(e2) + 1

For each successive iteration of the loop body the control variable, v,
takes successive values

e1, pred(e1), pred(pred(e1)), ..., e2

The following program uses both forms of for-statements to print the


alphabet forwards and backwards.

PROGRAM Alphabet(output);

(* Prints the alphabet forwards and backwards *)

VAR
xx : char;

BEGIN
FOR xx := 'a' TO 'z' DO
write(xx);
writeln;

FOR xx := 'z' DOWNTO 'a' DO


write(xx)
END.
A loop body need not refer to the control variable.

The following program raises two numbers to a specified power and the
control variable merely governs the number of iterations.

In this program it is also shown that a loop body may comprise several
statements if they are bracketed to form a compound statement.

PROGRAM Numston(input, output);


(* Raises two numbers x and y to a non-negative power *)

VAR
x, xx, y, yy : real;
n, power : integer;

BEGIN
write('Please give me two numbers: ');
readln(x, y);
write('To what power would you like it raised? ');
readln(n);

(* Initialize auxiliary variables *)

xx := 1;
yy := 1;

(* Start computations *)

FOR power := 1 TO n DO
BEGIN
xx := xx * x;
yy := yy * y
END;
writeln(x:1:4, ' raised to the power ', n:1, ' is ', xx :9:4);
writeln(y:1:4, ' raised to the power ', n:1, ' is ', yy :9:4)
END.
When one loop occurs within the body of another, the loops are said to be
'nested'.

This is illustrated in the following program which writes to a file the


multiplication tables from 1 to a given number n.

PROGRAM Multable(input, f);


(* Writes to file MULTIPLI.$$$ the multiplication
tables from 1 to a given number n *)

CONST
max = 10;

VAR
f : text;
i, j, n : integer;

BEGIN
write('Input the number of the maximum multiplication
table: ');
readln(n);

assign(f, 'MULTIPLI.$$$');
rewrite(f);
writeln(f, 'MULTIPLICATION TABLES UP TO ', n:1);
FOR i := 1 TO n DO
BEGIN
writeln(f);
writeln(f, 'TABLE OF ', i :2);
writeln(f);
FOR j := 1 TO max DO
writeln(f, i :2, ' * ', j :2, ' = ', i*j :3)
END;
close(f)
END.
6.3 While-statement
Pascal provides two non-deterministic loops: a while-statement and a
repeat-statement.

In this section is studied the while-statement.

The general form of this statement is

WHILE t DO s

where t is a test
and s is any statement

The test (t) is made 'prior' to each execution of the loop body (s).

If the test proves false, control leaves the while-statement and passes to
the next in sequence after the while-statement.

If the test holds true, the loop is obeyed and the process repeated until the
test eventually proves false.

If test is false upon first entry, the loop body is not entered at all.

The body of a while-loop need not be executed

So, here are some guidelines for using WHILE.

A while-loop is appropriate when it is not known how many


times it is wanted the loop body obeyed
and
it may be 'not at all'

The loop body (s) may be composed of either one or several statements. In
the last case, it must be bracketed to form a compound statement.

Three steps must be performed in every program that uses a while-loop.


These are
- initialize the loop-control (implicit in test t) variable before the loop is
reached

- test the loop-control variable before each loop repetition

- update the loop control variable in the loop body (s)

If the first step is omitted, the initial test of the loop control-variable will
be meaningless. If the last step is omitted, the loop control variable value
cannot change and the loop will execute "forever".

The above facts are illustrated in the following program which finds and
prints the time required to double a given amount of money.
PROGRAM Banker(input, output);

(* Finds and prints the time required to double a given amount of


money *)

VAR
year : integer;
depos, balanc, rate, intrst, double : real;

BEGIN
write('Enter the deposit amount : ');
readln(depos);
write('Enter the interest rate as a decimal fraction: ');
readln(rate);

(* Initialize year, balanc, and double *)

year := 0;
balanc := depos;
double := 2 * depos;

(* Recompute balanc until the initial deposit amount is


doubled *)

WHILE balanc < double DO


BEGIN
year := year + 1;
intrst := balanc * rate;
balanc := balanc + intrst
END;
writeln('Deposit amount is doubled after ', year:1, 'years');
writeln('New balance is ', balanc:1:2)
END.
6.4 Comparison of while-loops and for-loops

It has been said that a while-loop is anon-deterministic one, however, as


shown in the next example, a while-loop can also be used to implement a
counting loop.

Consider the following while-loop used to find the sum of the first "n"
integers, and the respective for-loop

WHILE-LOOP FOR-LOOP

sum := 0; sum := 0;
nxtint := 1; FOR nxtint := 1 TO n DO
WHILE nxtint <= n DO sum := sum + nxtint;
BEGIN
sum := sum + nxtint;
nxtint := nxtint + 1
END;

The most obvious difference between the loops is that the while-loop is
longer.

This illustrates that the while-loop should not be used to implement a


counting loop.

The for-loop is easier to write and should be used when the number of
loop repetitions need can be determined before loop entry.

The main reason for the extra length of the while-loop involves the
manner in which the loop-control variable "nxtint" is manipulated:

- "nxtint" is set to a initial value of 1 (nxtint := 1)


- "nxtint" is tested before each loop repetition (nxtint <= n)
- "nxtint" is updated during each loop repetition
(nxtint := nxtint + 1)

Although these three steps are implicit in the for-loop, they must be
specified explicitly when the while-loop is used.
If the loop-control variable is not initialised properly before the while-
statement is reached, the loop-repetition test will be meaningless.

If the loop control variable is not updated, the loop-repetition test will
always be true and the loop will not be exited (infinite loop).

6.5 Repeat-statement

The second non-deterministic loop is the repeat-statement.

The general form is

REPEAT
s1;
s2;
...
sn;
UNTIL t

where t is a test
and s1, s2, ..., sn are any statements

The test (t) is made 'after' each execution of the loop body (s1, s2, ..., sn).

If the test holds true, the loop is terminated and control passes to the
statement next in sequence after the repeat-statement.

If the test proves false, control returns to the start of the loop body and the
whole process is repeated until the test eventually holds true.

Once the loop is under way, a while-loop and a repeat-loop are equivalent.
The difference is upon the first entry.

With WHILE, the test is made 'before' the first execution of the loop body;
with REPEAT, the test is made 'after' the first execution.
The body of a repeat-loop is obeyed at least once

Here are guidelines for using REPEAT.

A repeat-loop is appropriate when it is not known how many


times it is wanted the loop body obeyed
but
it must be 'at least once'

More than one statement can be included within the body of a repeat-loop
without being bracketed between BEGIN and END.

The above facts are illustrated in the following program which computes
the smallest Fibonacci number greater than some specified bound.

The first two terms of the Fibonacci sequence are 0 and 1. Each
successive term is the sum of the previous two.

The program insists that the bound be greater than 1 and it must therefore
be computed 'at least one' new term. So REPEAT must be used.
PROGRAM Minfib (input, output);

(* Computes the smallest Fibonacci number which exceeds a


specified bound *)

VAR
bound, term1, term2, term3 : integer;

BEGIN
write('Specify the bound: ');
readln(bound);
IF bound < 1 THEN
writeln(' *** bound is too small ***')
ELSE
BEGIN
term3 := 1;
term2 := 0;
REPEAT
term1 := term2;
term2 := term3;
term3 := term2 + term1
UNTIL term3 > bound;
writeln('Smallest Fibonacci number greater',
' than ', bound :1, ' is ', term3 :1)
END
END.
CHAPTER 7

True or False?

7.1 Boolean types

Boolean data types are those which can only take values "true" or 'false".
These are called 'Boolean values'.

This category of data includes constants, variables, functions and


expression of logic type. Hence a test following IF, WHILE and UNTIL is
particular case of a 'Boolean expression'.

The two values ("true" and "false") applied to Boolean data types for an
ordered set where "false" precedes to "true". "false" has ordinal number 0
and "true" has ordinal number 1. Consequently

"false" < "true"

Boolean values can be written to a text file but not read from one.

7.2 Boolean case selector

A case-statement can emulate an if-statement. The test becomes a Boolean


selector and the two statements become limbs prefixed by "true" and
"false".

CASE t OF
true : s1;
false : s2
END (* CASE *)

is equivalent to

IF t THEN s1 ELSE s2
7.3 Boolean constants
A user-defined constant may have type Boolean.

For example, if "const1" and "const2" are two constants taken values
"true" and "false", respectively, then they are declared as:
CONST
const1 = true;
const2 = false;

A common use of user-defined constants is to control the selection of


output statements, particularly during the development of a program.

7.4 Boolean variables

A Boolean variable can take either of the two values "true" and "false" and
can acquire one of these values directly

VAR
b1, b2 : boolean;
...
b1 := true;
b2 := false;

or via a Boolean expression

VAR
negative, posdisc, factorfound,
usersaysno, dhasreachedn : boolean;
...
negative := digit < 0;
posdisc := disc > 0;
factorfound := n MOD d = 0;
usersaysno := reply = 'n';
dhasreachedn := d = n
In the following program is applied these facts. This program prints two
supplied three-letter words in alphabetical order.

PROGRAM Two3lett (input, output);


(* Prints two supplied three-letter words in alphabetical order *)

VAR
a1, a2, a3, b1, b2, b3 : char;
ab : boolean;

BEGIN
writeln('Type two three-letter words, one per line:');
readln(a1, a2, a3);
readln(b1, b2, b3);

IF a1 < b1 THEN ab := true


ELSE
IF a1 > b1 THEN ab := false
ELSE
IF a2 < b2 THEN ab := true
ELSE
IF a2 > b2 THEN ab := false
ELSE
ab := a3 <= b3;

CASE ab OF
true : writeln(a1, a2, a3, ' ', b1, b2, b3);
false : writeln(b1, b2, b3, ' ', a1, a2, a3)
END (* CASE *)
END.

A Boolean variable can obviously be used to record the result of a test


made within a loop.
7.5 Boolean functions

There are three standard functions which produce a Boolean result.

odd(x) - it becomes "true" if x is odd and "false"


otherwise. x must be integer

eoln(f) - it becomes "true" if there is no more


information to be read in the current line
of file f and "false" otherwise. When file
is "input", the parameter f may be omitted

eof(f) - It becomes "true" if there is no more


information to be read in file f and "false"
otherwise. When file is "input", the
parameter "f" may be omitted

The following program copies file "FILE1.$$$" onto file "FILE2.$$$".


PROGRAM Copyfile (f1, f2);

(* Program to copy "FILE1.$$$" onto "FILE2.$$$".It says the


number of character and if the number of lines is either
even or odd *)

VAR
f1, f2 : text;
ch : char;
licount, chcount : integer;

BEGIN
writeln;
writeln('COPYING "FILE1.$$$" to "FILE2.$$$" ...');

(* Initialize file f1 and file f2 *)

assign(f1, 'FILE1.$$$');
assign(f2, 'FILE2.$$$');
reset(f1);
rewrite(f2);

(* Initialize counter of lines and counter of characters *)

licount := 0;
chcount := 0;
(* Start copy *)

REPEAT

(* Making copy of each line *)

REPEAT

(* Counting characters *)

chcount := chcount + 1;

(* Making copy of each character *)

read(f1, ch);
write(f2, ch)
UNTIL eoln(f1);

(* Counting lines *)

licount := licount + 1;
UNTIL eof(f1);

(* Display results *)

writeln('COPY FINISHED');
write('The number of lines is');
IF odd(licount) THEN
writeln(' odd and equals ', licount:1)
ELSE
writeln(' even and equals ', licount:1);

write('The number of characters is');


IF odd(chcount) THEN
writeln(' odd and equals ', chcount:1)
ELSE
writeln(' even and equals', chcount:1);

(* Close files *)

close(f1);
close(f2)
END.
7.6 Boolean operators

If a Boolean expression is preceded by the operator

NOT

its value is 'inverted'.

If "b" has the value "true" then "NOT b" is "false"; if "b" has the value
"false then "NOT b" is "true".

Boolean operators can be combined using two Boolean operators

AND
OR

If b1 and b2 are Boolean

b1 AND b2 is "true" only if b1 'and' b2 are both "true"

b1 OR b2 is "true" if either b1 'or' b2 is "true" (or both


are "true")

Each of these operators has different precedence. Their precedence


'decreases' in the order

NOT { highest precedence }

AND { same precedence as DIV MOD * / }

OR { same precedence as + - }

In order to avoid errors when programming, bracketing must be used.


Then, if b1 and b2 are Boolean

b1 OR b2 must be written as (b1) OR (b2)

b1 AND b2 must be written as (b1) AND (b2)

NOT b1 must be written as NOT (b1)

All brackets are necessary because of the relative priorities of operators.

7.7 Set membership

Pascal allows the construction of 'sets'.

Square brackets, [ and ], are used as 'set constructors' and members are
separated by commas.

All members of a set must have the same type, and this must be an ordinal
type (and, hence, not "real"). This is called the base type of the set.

A set member may be represented by any expression of the base type.


Here are some examples

[1, 2, 3, 5, 7, 11] { set of integers }

['a', 'e', 'i', 'o', 'u'] { set of characters }

[i+1, ord('A')-1] { set of integers }


A short hand notation is available for a contiguous group of set members.
The set

[4, i-2, i, 6, i+1, i-1, i-4, i+3, 5, i+2]

can be written

[4..6, i-2..i+3, i-4]

Multiple occurrences of a value are equivalent to a single occurrence of


that value. The set

['C'..'H', 'W', 'E', 'F'..'K', succ('A')]

is the set

['C'..'K', 'W', succ('A')]

Set membership can be detected with the relational operator

IN

The right hand operand must be a set and the left hand operand must be an
expression whose type is the same as the base type of the set. The operator
yields "true" only if the value of the left hand operand is currently a
member of the right hand set.

For example the best way to check that an integer is in the range 1 to 9 is
with a set:

IF digit IN [1..9] THEN ...

One final note: sets are 'implementation dependent'. Both the maximum
size of a set and the range of ordinal numbers that set members may
possess vary from one computer to another.
CHAPTER 8

A Matter of Routine

8.1 Some routine definitions and concepts

Procedures and functions are called routines.

A routine is essentially a small piece of program with an associate name.

Routines offer two benefits:

1) Transparency is improved when the purpose of a group of statements is


indicated by a single.

2) Duplication is avoided when one routine is "called" several times.

A procedure is a routine which causes some change. Consequently, a procedure


call constitutes a statement.

A function is a routine which causes no change but computes a value.


Consequently, a function call constitutes an expression.

Some standard procedures have already been found (write, writeln, read,
readln, etc), as well as some functions (sin, cos, sqrt, odd, etc).

Pascal allows to extend the number of routines available by declaring user's


own.

Routines must be declared between the variable declarations (if there are any)
and the BEGIN of the program body.
8.2 Procedures

A simple procedure has the basic form

PROCEDURE p;
BEGIN
s1; s2; ...; sn
END (* p *);

where p is the procedure name


and s1, s2, ..., sn are the statements (these constitutes the
procedure body

The procedure can be "called" from the main program by simply quoting its
name. This gives the "procedure call statement". When a procedure is called,
the statements of the procedure body are obeyed and the control returns to the
statement following the call.

As an example of the use of procedures consider the program to copy on file


onto other (see Program Copyfile in Chapter 7). This program consists of
several actions:

Initialize files 1 and 2 (file 1 to be read and file 2 to be written)


Initialize counter of lines and counter of characters of file 1
Start copy from file 1 to file 2
Making a copy of each line from file 1 to file 2
Making a copy of each character from file 1 to file 2
Display results
Close files

The complete program might have the following structure, where the actions
are declared as procedures:
PROGRAM Copyfilp (f1, f2);

(* Program to copy "FILE1.$$$" onto "FILE2.$$$". It says the


number of character and if the number of lines is either even
or odd *)

VAR
f1, f2 : text;
ch : char;
licount, chcount : integer;

(* ***************************************************** *)

PROCEDURE Inifiles;

(* Initialise file f1 and file f2 *)

BEGIN
assign(f1, 'FILE1.$$$');
assign(f2, 'FILE2.$$$');
reset(f1);
rewrite(f2)
END; (* initialization of files *)

(* ***************************************************** *)

PROCEDURE Inilicha;

(* Initialize counter of lines and counter of characters *)

BEGIN
licount := 0;
chcount := 0
END; (* initialization of counters *)
(* ***************************************************** *)

PROCEDURE Copychar;

(* Copy each character from file 1 to file 2 *)

BEGIN
REPEAT

(* Counting characters *)

chcount := chcount + 1;

(* Making copy of each character *)

read(f1, ch);
write(f2, ch)
UNTIL eoln(f1)
END; (* copy of characters *)

(* ***************************************************** *)

PROCEDURE Copyline;

(* Making copy of each line from file 1 to file 2 *)

BEGIN
REPEAT
copychar;
licount := licount +1
UNTIL eof(f1)
END; (* copy each line *)

(* ***************************************************** *)
PROCEDURE Copyfile;

(* Start copy form file 1 to file 2 *)

BEGIN
copyline
END; (* copy of lines *)

(* ***************************************************** *)

PROCEDURE Dispresu;

(* Display results *)

BEGIN
writeln('COPY FINISHED');
write('The number of lines is');
IF odd(licount) THEN
writeln(' odd and equals ', licount:1)
ELSE
writeln(' even and equals ', licount:1);
write('The number of characters is');
IF odd(chcount) THEN
writeln(' odd and equals ', chcount:1)
ELSE
writeln(' even and equals', chcount:1)
END; (* display results *)
(* ***************************************************** *)

PROCEDURE Clofiles;

(* Close files *)

BEGIN
close(f1);
close(f2)
END; (* close files *)

(* ***************************************************** *)

BEGIN (* Main Program *)


writeln;
writeln('COPYING "FILE1.$$$" onto "FILE2.$$$" ...');
Inifiles;
Inilicha;
Copyfile;
Dispresu;
Clofiles
END.

Notice in this program that one procedure may call another. The only
restriction is that the declaration of a procedure must precede its calls.

A procedure may even call itself. It is then said to display "recursion" but this is
beyond the scope of the present lecture notes.
8.3 Localized information

It have been seen that a program can contain declarations specifying entities
which may be used by procedures which are declared within the program and
by statements of the program body.

The same applies to procedures. A procedure declaration may contain its own
declarations.

Any entity declared within a procedure may be used by any procedure declared
within the procedure and by statements of the procedure body.

Declarations within a procedure appear between the PROCEDURE line and the
BEGIN of the procedure body and are subject to the same rules of order as in
the main program.

An entity declared within a procedure is said to be "local" to the procedure.

An entity declared in the main program is said to be "global".

Storage space for local variables is allocated at run-time when a procedure is


entered and released when the procedure is exited. So, even if two variables in
different procedure have the same name, they will refer to different storage
locations at run-time.

It is good practice to declare variables as locally as possible. This reduces the


possibility of their corruption in other part of the program. As an example of
the above remarks consider the following program.
PROGRAM Locvar(input, output);

(* Program to shows the use of local and global variables *)

CONST
k = 10; (* this is a global constant *)

VAR
ch : char; (* this is a global variable *)

(* ***************************************************** *)

PROCEDURE Writchar;

CONST
m = 5; (* this a local constant *)

VAR
i, km : integer; (* these are local variables *)

BEGIN
km := k - m;
writeln('The character ', ch,' repeated ', k-m,' times is:');
FOR i := 1 TO km DO
writeln(ch)
END;

(* ***************************************************** *)

BEGIN (* MAIN PROGRAM *)


writeln;
write('Type any character: ');
readln(ch);
writchar;
readln
END.

8.4 Functions
The fundamental difference between a function and a procedure is that a
function call constitutes an "expression" because a function "delivers" a "result"
whereas a procedure call constitutes a "statement" and no value is associated
with the call.

A function is a suitable form for a routine when one is interested in only one
value computed by the routine and the routine produces no "side effects".

A routine is said to cause a side effect if it produces any non-local change.

A function with side effects is undesirable because functions are used in


expressions and a program con be hard to follow if evaluation of a expression
causes no-local changes.

In Pascal, a function may deliver a result of any ordinal type (boolean, char,
integer), of type real, on of any other type defined by the user (these are defined
in future chapters).

A function declaration differs from a procedure declaration in three respects:

1. -FUNCTION replaces PROCEDURE.

2. The type of the result delivered by the function is quoted


(preceded by a colon) before the closing semicolon of the
function heading.

3. Within the function body, there must be at least one


assignment statement with the function identifier on the
left hand side.

As an example consider the following program for raising a number x to an


integer power n.

PROGRAM Raisxton (input, output);

(* Raises a number a to en integer power n *)


VAR
x : real;
n : integer;

(* ********************************************* *)

FUNCTION xtothen : real;

VAR
xsquared, p : real;
power : integer;

BEGIN
If n = 0 THEN
xtothen = 1
ELSE
BEGIN
xsquared := sqr (x)
IF odd (n) THEN
p := x
ELSE
p := 1;
FOR power := 1 TO n DIV 2 DO
p := p * xsquared;
xtothen := p
END
END (* to then n *) ;

(* ********************************************* *)

BEGIN (* main program *)


write('Please give me a number: ');
readln(x);
write('To what power would you like it raised: ');
readln(n);
writeln(x, ' raised to the power ', n :1, ' is ', xtothen);
readln
END.

In order to give more examples illustrating the use of functions, consider the
following two programs

PROGRAM Insuranc (input, output)


(* Categorizes an age into one of five groups *)

VAR
age : integer;

(* **************************************************** *)

FUNCTION groupforage : char;

BEGIN
CASE age DIV 5 OF
3: groupforage := 'A';
4, 5: groupforage := 'B';
6..9: groupforage := 'C';
10..12: groupforage := 'D'

ELSE
groupforage := 'F
END; (* case *)
END; (* group for age *)

(* **************************************************** *)

BEGIN (* main program *)


write('Supply an age: ')
readln(age);
writeln('Insurance group is: ', groupforage);
readln
END.
PROGRAM Word3fns (input, output);
(* Prints two supplied three-Letter words in alphabetical order *)

VAR
a1, a2, a3, b1, b2, b3 : char;
(* ************************************************** *)

FUNCTION abinorder : boolean;

BEGIN
IF a1 < b1 THEN
abinorder := true
ELSE
IF a1 > b1 THEN
abinorder := false
ELSE
IF a2 < b2 THEN
abinorder := true
ELSE
IF a2 > b2 THEN
abinorder := false
ELSE
abinorder := a3 <= b3
END; (* a b in order *)

(* ************************************************** *)

BEGIN (* main program *)


writeln('Type two three-Letter words, one per line:');
readln(a1, a2, a3); readln (b1, b2, b3);
CASE abinorder OF
True : writeln (a1, a2, a3, ' ', b1, b2, b3);
false: writeln (b1, b2, b3, ' ' , a1, a2, a3)
END; (* CASE *)
readln
END.
CHAPTER 9

Routine Information

9.1 Transference of parameters

A program may transfer information to a procedure or obtain it from it.

Assume, for the moment, that a value is going to be passed to a procedure. As


an example, consider the following program having a procedure inside it
exhibiting the result in a Boolean expression, as for example "yes" or "not"
instead "true" or "false". Notice in this program that the Boolean value
"booleanvalue" is a formal parameter used as a variable into the procedure.

PROGRAM Eor (input, output);

(*Simulates the "exclusive or" operation *)

VAR
a, b, eo : boolean;
inta, intb : integer;

(* ********************************************** *)

PROCEDURE Yesno (booleanvalue : boolean );

(* Procedure to exhibit a boolean value *)

BEGIN
CASE booleanvalue OF
true : write ('YES');
false: write ('NOT')
END (* CASE *)
END; (* Yesno *)

(* ********************************************** *)
BEGIN (* Main program *)
REPEAT
write ('Enter two integer values, 0 is no: ');
readln (inta, intb);
a := true;
IF inta = 0 THEN
a := false;
b := true;
IF intb = 0 THEN
b := false;

(* Eco of the input values *)

write ('You input a ');


yesno (a);
write (' and a ');
yesno (b);
writeln;

(* Process and prints out an eor b *)

eo := (a AND NOT b) OR (b AND NOT a );


write ('The exclusive or of these ones is: ');
yesno (eo);
writeln
UNTIL false;
readln
END.
In general, a list of parameters gives the local names of the parameters as well
as their types. These are called "formal parameters" of the procedure:

PROCEDURE Byexample (i,j: integer; maybe:


boolean; b,c,g: real).,

Notice that the formal parameters are grouped by types.

When a procedure is used, the correct number of parameters must be given as


well as their corresponding types.

9.2 Value and variable parameters

In the example given in the preceding section, a value was transferred to a


procedure. But it is also frequent to transfer a value in the inverse way.

Pascal distinguishes two types of parameters:

value parameters: transfer values to a procedure

Variable parameters: transfer values from a procedure

Consider the following program:


PROGRAM Cubevalu (input, output);

(* Shows a value parameter *)

VAR
test : integer;

(* ********************************************** *)

PROCEDURE Cube (number : integer);

(* computers the cube of a given number *)

BEGIN
number := number * number * number;
writeln ('The cube of the number is: ', number)
END; (* Cube *)

(* ********************************************** *)

BEGIN (* Main program *)


writeln;
write ('Enter an integer value: ');
readln (test);
writeln ('The test value is: ', test);
cube (test);
writeln ('Now the value of test is: ', test);
readln
END.

The above program calls a procedure having a value parameter.

The cube of 3, which is 27, is given when the program is run with 3 is given as
input, but when the procedure has finished the value of "test" is still 3.
Therefore, if a procedure modifies a value parameter then only its own
temporary copy is modified.
The above program has modified its own local copy, called "number", but
"test" has not been modified.

Consider now the following modification of the above program:

PROGRAM Cubevari (input, output);

(* Shows a variable parameter *)

VAR
test : integer;

(* ********************************************** *)

PROCEDURE Cube (VAR number : integer);

(* Computers the cube of a given number *)

BEGIN
number := number * number * number;
writeln ('The cube of the numbers is: ', number);
END;

(* ********************************************** *)

BEGIN (* Main program *)


write ('Enter an integer value: ');
readln (test);
writeln ('The test value is: ',test);
cube (test);
writeln ('Now the test value is: ',test);
readln
END.

The reserved word "VAR" in the list of parameters specifies that "number" is a
variable parameter.
When the procedure modifies "number", the variable "test" is also modified.
This is an important difference.

A mixed group of value and variable parameters con be represented as follows.

PROCEDURE ZAMP (VAR a,b: integer., x: real., VAR


booby: boolean)

Here x is a real value parameter. The other ones are variable parameters.

For the case of functions, these may take parameters, they are always value
parameters.

As an example consider the following two programs using functions, which in


turn use value parameters.
PROGRAM Powers (input, output);
(* Given x, y, m and n, the program forms
x^m, y^n, (x^n)^m, (y^m)^n *)

VAR
x, y : real;
m, n : integer;

(* ********************************************* *)

FUNCTION powered (x : real; n : integer) : real;

VAR
i : integer;
p, xsquared : real;

BEGIN
IF n = 1 THEN
powered := x
ELSE
BEGIN
xsquared := sqr (x);
IF odd (n) THEN
p := x * xsquared
ELSE
p := xsquared;
FOR i := 2 TO n DIV 2 DO
p := p * xsquared;
powered := p
END
END; (* powered *)
(* ********************************************* *)

BEGIN (* program body *)


writeln('Give me two real numbers and two positive
integers: ');
readln(x, y, m, n);
writeln(x:1:2, ' raised to the power ', m:2, ' = ',
powered (x,m):1:2);
writeln(y:1:2, ' raised to the power ', n:2, ' = ',
powered (y,n):1:2);
writeln(x:1:2, ' raised to the power ', n:2,'
all raised to the power ', m :2, ' = ',
raised powered (x,n), m):1:2);
writeln (y:1:2, ' raised to the power ', m :2,
' all raised to the power ', n :2, ' = ',
powered (powered (y,m), n):1:2);
readln
END.
9.3 Forward references

It was stated at the end of section 8.2 that the declaration of a procedure must
precede its calls. Pascal provides a relaxation of this rule: A "routine heading"
must be declared before the routine is called but its "body" may be declared
later (but in the same block of procedures). The declaration of the heading
takes the form of a "forward reference". The routine heading is presented but
its body is replaced by

forward;

When its body is declared later, it is preceded by an abbreviated heading. Only


the routine identified appears following either PROCEDURE or FUNCTION.
No mention is made of any parameters, nor in the case of a function, nor the
type of the function.

In order to illustrate the above remarks, suppose procedures A and B are given
as shown next:

PROCEDURE A (a,b: integer);


BEGIN (* A *)

END; (* A *)

PROCEDURE B (x,y : real);


BEGIN (* B *)
A
END; (* B* )

Since A is defined first, it can be called from B., however, B may not be called
from A. According to the remarks given in order to make B callable from A,
two changes must be made in the procedures as written:

I) The heading line of procedure B, with the reserved word "forward" and a
semicolon appended, is written before the definition of procedure A.
II) The other change is in the actual heading line that accompanies procedure
B; it is simple written

PROCEDURE B;

With no indication of the parameters. No other change is made in procedure B.

Therefore the above procedure must be written as follows:

PROCEDURE B (x,y: real); forward;

PROCEDURE A (a,b: integer);


BEGIN (* A *)

END; (* A *)

PROCEDURE B;
BEGIN (* B *)

END; (* B *)
CHAPTER 10

Give it a Name

10.1 Introduction: types resumed

In the preceding chapters it has been studied "real"; "integer", "boolean" and
"char" types.

The types "real", "integer" and "char" can be written to and read from a text
file.

Boolean types can be written to a text file but not read from one.

"Integer", "boolean" and "char" types form an ordered set. This means that they
can be used for selection in a case-statement or as an index in a for-loop.

For any ordered type the following functions are well defined:

succ (x) successor of variable x


pred (x) predecessor of variable x

For example:

succ (3) = 4;
pred ('q') = 'p'
succ (false) = true;
succ (true) gives an error

Pascal allows to define user-defined types. In subsequent chapters it shall be


seen how data can be grouped in different ways; in this chapter it shall be learnt
how to define new types to represent scalars (single items).
There are two classes of user-defined scalar types: "subrange" types and
"symbolic" types. Both play a mayor role in improving the compile-time
security of a program, both contribute to program efficiency and symbolic
types in particular make significant contribution to a program transparency.

A series of type definitions may appear between the constant declarations (if
there are any) and the variable declarations (if there are any), heralded by the
reserved word TYPE. Thus a "type definition" has the form.

TYPE
i = t;

where i is an identifier
and t is a type.

Thus the order of declarations within any program is.

Constants, Types, Variables, Procedures and functions.

10.2 Subrange types

A variable of ordinal type, boolean, char, integer and symbolic (this will be
studied in the next section) can be restricted to a subrange. The variable is then
said to have a subrange type but inherits all the properties of its "host" type.

Any attempt to give a subrange variable a value outside its permitted range
constitutes an error. Notice that subranges or type "real" are not allowed.

A subrange type is denoted by the upper and lower bounds separated by two
dots. These bounds must be constants and the ordinal number of the lower
bound must not exceed the ordinal number of the appear bound.

Here are some subrange types.

1.. maxint
'A'..'Z'
-1..17

Subrange types may be defined in terms of user defined constants (see


examples below).

Subrange types allow the user to check the sensible use of variables. From the
"problem domain" the user decides what range of values should be sensible for
each variable and specify this range in the declaration of the variable. Any
attempt to assign a value outside the expected range will be prevented.

Using subranges the user helps the computer to detect his or her mistakes.
"Minimal subranging" should always be the aim.

As examples, consider the subranges that should have been used in some
previous examples.

I) An age cannot be negative and is unlikely to exceed, say, 120:

CONST
maxage = 120;

VAR
agenow: 0..maxage;
II) For any practical box, the length of each side must exceed 0:

VAR
length, width, depth: 1..maxint;

III) In the program which asks for initials A,B,C, or F the minimal subrange is
A to F:

VAR
initial: 'A'..'F'

IV) If a program ask for a digit it is more convenient the following


implementation:

TYPE
digits = 0..9;

VAR
digit : digits

PROCEDURE Name (d: digits);

V) Within a function which raises a real number to a positive power "n", the
parameter "n" cannot be negative and so should be constrained to the natural
numbers:

TYPE
naturals = 0..maxint;

VAR
m,n: naturals;

FUNCTION Powered (x: real; n: naturals): real;

On the other hand, subrange types provide a valuable protection against


programming errors and can also be used to detect data entry errors.
Obviously programs which "crash" when given bad input are not to be
encouraged so, for input data validation, it is often better to use range types to
read values and then assign these values to subranges variables only when it is
known that the values are in range. For example to real a digit, it could be used
two variables:

VAR
d: 0..9;
dtyped: integer;

And then read values until one is in range:

REPEAT
write ('please type a digit ' );
readln (dtyped)
UNTIL dtyped IN [0..9];
d: = dtyped

Of course, even this is not completely safe because an execution error will
result if anything other than an integer is typed.

10.3 Symbolic types

A symbolic type (or "enumerated" type as it is often called) is an ordered type.


This type is user-defined according to the results or values expected.

A symbolic type is denoted by a bracketed list of user-defined names. For


example:

TYPE
rainbow = (red, orange, yellow, green, blue, violet);

Where the type rainbow has six given values. In this example:

ord (red) = 0
ord (orange) = succ (red) = 1
Variables of type "rainbow" can be created:

VAR
pallet: rainbow;

A symbolic type can be used as a limb in a case-selector, or as index in a for-


loop.

In the above example, elements of the type "rainbow" can be assigned to


variables of the same type or compared among them, they also can be
transferred as parameters in procedures or functions.

Unfortunately, as was said before, symbolic type cannot be written to or read


from files. Thus the value "red" is not the chain of characters "red", it is simply
"red". However, it can be written ord (red) which is 0, and can be printed out
chains for the values of type "rainbow" making an appropriate procedure.

The above remark is illustrated in the following example. In this example is


presented the use of a symbolic type which represents the months of the year.
The programs asks for input a month number and outputs the month name.
There are several ways for implementing the program, but the example
illustrates a problem when using symbolic types. For the "char" type, the
function

chr (integer value)

is opposite to function

ord (character value)

As it was said, function "ord" can be used for symbolic types, however there is
not opposite to it in symbolic types. This program shows how to construct the
opposite to "ord" for symbolic types: unord (integer) must be the opposite to
ord (month).
PROGRAM Months (output);
(* Shows the conversion of elements of symbolic type: an integer is
transformed to a month and the month is displayed *)

TYPE
month = (jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov,
dec);
numonth = 1..12;

VAR
testmonth : month;
(* ********************************************** *)
PROCEDURE Givemonth (Thismonth : month);
(* Displays the full month name *)

BEGIN
writeln;
write ('This month is ');
CASE thismonth OF
Jan : writeln (' January ');
Feb : writeln (' February ');
Mar : writeln (' March ');
Apr : writeln (' April ');
May : writeln (' May ');
Jun : writeln (' June ' );
Jul : writeln (' July ');
Aug : writeln (' August ');
Sep : writeln (' September ');
Oct : writeln (' October ');
Nov : writeln (' November ');
Dec : writeln (' December ');
ELSE
writeln('not a month')
END; (* CASE *)
writeln
END;
(* ********************************************** *)
FUNCTION Ordmonth (imonth : numonth): month;
(* Converts an ordered integer to a month type *)

VAR
domonth : month;
index : numonth;

BEGIN
domonth := jan;
FOR index := 2 To imonth DO
domonth := succ(domonth);
ordmonth := domonth
END;

(* ********************************************** *)

PROCEDURE Takemonth (VAR Thismonth : month);


VAR imonth : numonth;

BEGIN
writeln;
write ('Enter the number 1..12 of a month: ');
readln (imonth);
Thismonth := ordmonth (imonth)
END;

(* ********************************************** *)

BEGIN (* Main program *)


REPEAT
Takemonth (testmonth);
givemonth (testmonth);
UNTIL false
END.

Subranges of symbolic types can be defined, for example with the type
"month":
TYPE
month = (jan, feb, mar, apr, may, jun, jul, aug, sep, oct,
nov, dec);

Subranges are created inside the "type" declaration itself, or in the "type"
declaration of a contained block.

halfore: jan .. jun;


halfore: jul .. dic:
CHAPTER 11

File it

11.1 Review of text files

As a data structure, a "file" is an ordered collection of related data items,


usually stored in external memory, for input to or output by a program; that is
information can be read from the file and/or written to the file.

The files used in Pascal programs up to this point have been "text files", whose
components are characters and these characters are organized into lines.

The predefined file type "text" is a standard type identifier used to declare text
files. The standard system files "input" and "output" associated with the
standard input (keyboard) and output (screen) devices are examples of text
files, and in Chapter 5. user-defined text files were described. The predefined
procedures "readln", "writeln", and "eoln" may be used only with text files.

Pascal also supports nontext files, whose components may be of any type,
simple or structured, except that they may not be of file type. Although
attention in this section is restricted to text files, much of the discussion also
applies to the other types of files considered in subsequent sections.

The principal rules governing the use of text files in Pascal programs as
discussed in Chapter 5 may be summarized as follows:

1. Program heading. It is good practice to include the names of all


files used in a program (including the standard text files input and
output) in the file list of the program heading.

2. Declaration. Each user-defined file must be declared as a file


variable in the declaration part of the program. For text files, the
predefined type identifier "text" can be used to specify the types of file
variables.
3. Associating file variables with actual files: In Pascal, before a file can
be used for input or output in a program, it must be associated with a disk file
by calling the predefined procedure "assign" in a statement of the form.

assign (file-variable, file-name);

4. Opening files for input. Each file from which data is read must first be
opened for input with the predefined procedure "reset" in a statement of the
form.

reset (file-variable);

Each such procedure call results the data pointer to the beginning of
the specified file. (The standard system file "input" need not be opened
for input.)

5. Each file to which data values are to be written must first be opened
for output. For text files, this can be done by using the predefined
procedure "rewrite" in a statement of the form.

rewrite (file-variable);

6. File input: Information can be read from a text file by using the
predefined procedures read and readln in the forms

read (file-variable, input-list)


readln (file-variable, input-list)

If file-variable is omitted, values are read from the standard system


file "input".

7. File output: Output can be directed to a text file by using the


predefined procedures write and writeln in the forms.

write (file-variable, output-list)


writeln (file-variable, output-list)
If file-variable is omitted, values are written to the standard system
file "output".

8. Closing files: In Pascal, after output to a file is completed, the file


should be closed by calling the predefined procedure "close" with a
statement of the form.

close (file-variable)

Failure to do so may result in the loss of data values because they


may not be transferred from the output buffer to the file.

9. Copying files: The contents of one file cannot be copied to another


file by using an assignment statement of the form "file- variable-1:= file-
variable-2". Rather the components must be copied one at a time.

10. Files as parameters: Formal parameters that represent files must


be variable parameters. This is a consequence of Rule 9, since value
parameters require copying the values of the corresponding actual
parameters.
11.2 Nontext files

While text files are frequently used in programming applications, "nontext file"
are sometimes more convenient.

Data written to a nontext file are transcribed, bit by bit, in exactly the same
form that they have within the computer. This has several advantages over text
files.

The first advantage is speed. Because there is no need to make conversions


from one form to another, the reading and writing are much faster.

A second advantage is a wider range of types that may be written to a file or


read from it.

Only characters, integers, and real numbers may be read from a text file; it can
be printed only these three types and boolean variables. However, anything that
can be represented within the computer can be written to or read from a nontext
file.

Nontext files have some disadvantages, too. The cannot be printed because
they are not in a printable (i.e., character) form. Similarly, they cannot be
created by an editor as a text file is. There are also certain restrictions on the
reading and writing that will be considered later in this section.

The above disadvantages suggest the uses for nontext files. It might no want to
have the output printed or displayed on the screen when the output is simply
used as the input for another (or the same) program: As already seen in Chapter
2 the output of the compiler is the input for the loader. Such output is not given
in a readable (that is, readable by humans) form.
Now let consider their declaration and use. In order to declare such a file, it is
used

VAR
filename: file of type;

or alternatively,

TYPE
typename = file of type;

VAR
filename: typename;

The "type" here can be almost any type: the simple types, arrays (chapters 12
and 13), or record (chapter 14) types. The only restriction is that it may not be
a file type or a record type with a file type component.

For example, the declarations

TYPE
file1 = file of integer;
file2 = file of char;
file3 = file of boolean;

VAR
f1 : file1;
f2 : file2;
f3 : file3;

are valid definitions of files.


It can be written to these files and read from them in much the same way as
with text files. Nevertheless, there are some differences, centering around the
fact that nontext files are not organized into lines as text files are. Thus, the
procedures readln and writeln and the function eoln cannot be used with
nontext files. The remaining functions and procedures relating to input and
output-reset, rewrite, read, write, and eof-are used in much the same way as
they were with text files.

While the types "text" and "file of char" might appear to be equivalent, there
are significant differences. The declaration

VAR
FileA : file of char;

does not declare FileA to be a text file; it is imply a nontext file containing only
characters. It is nor organized into lines: so we cannot use readln, writeln, on
eoln. Furthermore, a write statement will not convert numeric or boolean
values into strings of characters; nor will a read statement convert a string of
numeric character into an integer or real value. If it is given the command

READ
(FileA, x) or write (FileA, x)

With x declared to be anything other than a character type, it would result in an


error.

as an example of the use of nontext files, consider the following program:


PROGRAM filesrw (data, output);

(* This program creates a file of reals and


writes data to it and read data from it *)

VAR
data: FILE OF real;
value, square1, square2, root1, root2 : real;
count : integer;

BEGIN
assign(data, 'data.$$$');

(* write data to file *)

rewrite(data);
FOR count := 1 TO 10 DO
BEGIN
value := count;
square1 := sqr(value);
root1 := sqrt(value);
write( data, value, square1, root1)
END;

(* read data from file and display them to VDU *)

reset(data);
WHILE NOT eof(data) DO
BEGIN
read(data, value, square2, root2);
writeln(value :6:2, square2 :8:2, root2 :6:2)
END;
readln
END.
CHAPTER 12

Line up

12.1 One-dimensional arrays

So far, real and integer variables have been used for representing just one value
reach one. However there are a lot of situations for which a program must have
a list of variables of the same type represented by one only identifier.

In a mathematical presentation using many variables, subscripted variables are


often used; for example, x1, x2, x3,..., x [100]. Since anything cannot be
written above or below the line in programming languages, Pascal uses a
somewhat different notation. In Pascal, the notation is

x [1], x[2], x[3],..., x[100]

This is called an "array". More specifically, a "one-dimensional array" is finite


sequence of variables all of the same type and all identified by the same name.

One variable of an array is distinguished from another by its "subscript" or


"index", which is the value of an expression given within brackets and
following the array identifier.

In general, a subscripted (or indexed) variable has the form

identifier [expression]
The identifier is subject to the normal restrictions of identifiers; in particular,
there may be no space within it. Nevertheless, there may be one or more spaces
between the identifier and the left bracket, between the left bracket and the
expression, or between the expression and the right bracket.

The variables used above may be declared by (if they are of integer type)

VAR
x: ARRAY [1..100] OF integer

An alternative way of declaring the above array is by a named type-(that is, by


first declaring a type, which is then used to declare the variable.). Thus, it
might have.

TYPE
arraytype = ARRAY [1..100] OF integer;

VAR
x: arraytype;

"Type declarations" must precede variable declarations and follow constant


declarations.

Pascal requires named, rather than anonymous, types when passing arrays as
parameters.

A subscripted variable refers to a single storage location in the array and,


therefore, is used in much the same way as a simple variable. For example;
x [1] := 5;
list[7] := x[1] + y[7];
temp := list[7]
read (array [4])

While arrays allow us to define hundreds or thousands of variables with a


single declaration, their greatest advantage lies in the evaluation of the
subscript. It is not necessary to have a constant within the declared subscript
range. Whatever expression is within the brackets is evaluated, the result is the
subscript.

As example, suppose that

a[1] = 5, a[2] = 1, a[3] = 3, a[4] = 2, a[5] = 0,

b[0] = 4, b[1] = 2, b[2] = 3, b[3] = 1,

i = 3, j = 2

Then

a[1] = a[3] = 3,

b[j-1] = b[2-1] = b[1] = 2,

a[i+j] = a[3+2] = a[5] = 2,

a[i-j+1] = a[3-2+1] = a[2] = -1,

a[b[1]] = a[b[3]] = a[1] = 5,

b[a[j]] = b[a[2]] = b[-1] is not defined

a[b[a[j]+1]] = a[b[a[2]+1]] = a[b[0]] = a[4] = 2

It is this evaluation of the subscript that makes arrays so useful. With the added
facility of arrays the problem of reading a list of numbers and writing it in
reverse order is quite simply. This is show for a list of exactly 10 numbers in
the following program.

PROGRAM Readreve (input, output);

(* Reads a list of 10 integers and prints them in reverse order *)

TYPE
numarraytype = ARRAY [1..10] OF integer;

VAR
num : numarraytype;
index : integer;

BEGIN
writeln ('Give a list of 10 integers: ');
FOR index :=1 TO 10 DO
read (num[index]);

(* write the number in reverse order *)

writeln ('The reversed list is: ');


FOR index :=10 DOWNTO 1 DO
writeln (num[index]);
readln
END.
Arrays can sometimes be accessed in their entirety, rather than one component
at a time, in assignment statements or parameter passing. For example, if "a"
and "b" are declared identically, say

TYPE
realarray = ARRAY [1..25] OF real;

VAR
a,b: realarray;

Then
a: = b

is a valid assignment.

Similarly, if the following declarations are give

TYPE
arraytype = ARRAY[1..25] OF real;

VAR
a,b : arraytype;

and the procedure P parameter

VAR
c: arraytype;

Then the procedure P may be called by P (a) or P(b). When passing an entire
array, both the formal parameter and the actual parameter must be declared
using the same "named" type.

As an example, consider the following program

PROGRAM Findmax (input, output);


(* Shows the passing of arrays as a parameter*)

TYPE
demoray = ARRAY [1..4] OF integer;

VAR
one, two : demoray;
max, index : integer;

(* ********************************************** *)

PROCEDURE Takearray (VAR inarray : demoray);

(* An array is input *)

VAR
i : integer;

BEGIN
writeln ('Input 4 values for an array: ');
FOR i :=1 TO 4 DO
read (inarray [i]);
writeln
END;
(* ********************************************** *)

PROCEDURE Obtmax (sweeparray : demoray;


VAR value, position : integer);

(* Sweeps sweeparray in order to find its maximum value


as well as its position *)

VAR
i:integer;

BEGIN
value := sweeparray[1];
position := 1;
FOR i := 2 TO 4 DO
IF Sweeparray[i] > value THEN
BEGIN
value := Sweeparray[i];
position := i
END;
END;

(* ********************************************** *)

BEGIN (* Main program *)


writeln ('Find the maximum value of two arrays');
takearray (one);
takearray (two);
obtmax (one, max, index);
writeln ('The maximum of the first array is: ',max,
' at the index ', index);
obtmax (two, max, index);
writeln ('The maximum of the second array is: ', max,
' at the index ', index);
readln
END.
As it can be seen in the above program am array is passed as a variable
parameter. Procedure Takearray uses an array as a variable parameter since it
must return the array with its new values. Procedure obtmax uses a value
parameter; but it was not necessary. However, it is better to use a variable
parameter when using arrays. If a value parameter is used with arrays, the
computer must waste time and space to create the copies when a procedure is
called. Therefore

Always pass arrays as variable parameters

On the other hand, Pascal does not permit the following procedure or function
heading:

PROCEDURE Findmax (VAR arrent: ARRAY [1..10] OF


integer, big, where, : integer);

12.2. Sorting

Consider now the problem of sorting a list of n numbers according to a


sequence of increasing values. The program is going to be written in such a
way it does not use unnecessary memory; therefore it will contain a unique
array (one-dimensional, real and having x as name) where one element is going
to be reordered each time.

The method begins searching for in the array the lowest number and
exchanging it with the first element of the array. The lowest number will now
at the top of the list.

As a second step, the lowest number of the remaining n-1 numbers is going to
be searched and it will be exchanged with the second element of the array. The
second lowest number will be now the second in the list.

Continuing with this process, it will be searched for the lowest number among
the remaining n-2 numbers and it will be exchanged with the third element in
the array, and so on until all of the array be sorted.
It will be required a total of n-1 steps for sorting the array, but the length of
each search will be diminished at each step.

In order to find the lowest number at each step, each number x[i] in the array
will be sequentially compared to a starting number x[loc], where "loc" is an
integer variable used to "mark" one of the elements of the array.

If x[i] is lower than x[loc], both numbers are exchanged themselves, if not they
stay at their original places.

Once the above method has been applied to the complete array, the first
number will be the lowest one. Continuing with the above method, the process
is repeated n-2 times, until complete a total of n-1 steps (loc =1, 2,...,n-1).

The only question to be answered is how to exchange two numbers. Firstly, the
value of x[loc]. is reserved for future reference. After that the actual value of
x[i] is assigned to x[loc]. Finally, the "original value" of x[loc], which has been
previously reserved, is assigned to x[i]; the exchange of numbers has been
completed.

The above method can be implemented in a Pascal procedure, named


"exchange", as is shown as follows:
PROCEDURE Exchange;
(* Sorts the elements of an array *)

BEGIN
FOR loc := 1 TO n-1 DO
FOR i := loc + 1 TO n DO
IF x[i] < x [loc] THEN
BEGIN
temp := x[loc];
x[loc] := x[i];
x[i] := temp
END
END;

In the above procedure is assumed that "loc", "i" and "n" are integer variables
in the range 1 to 100. It is also assumed that "temp" is a real variable used to
temporarily storage x[loc]. Notice that the procedure uses nested for-loops.

Consider now the strategy for the general program:

1. Read the array size, n.


2. Read the n elements of the array.
3. Sort the elements of the array using the above procedure.
4. Write the sorted elements.

Here it is the complete program:


PROGRAM Sorting (input, output);

(* This program sorts a real one-dimensional array *)

VAR
n, i, loc : 1..100;
x : ARRAY [1..100] OF real;
temp : real;

(* ********************************************** *)

PROCEDURE Exchange;

(* Sorts the elements of an array *)

BEGIN
FOR loc := 1 TO n-1 DO
FOR i := loc + 1 TO n DO
IF x[i] < x[loc] THEN
BEGIN
temp := x[loc];
x[loc] := x[i];
x [i] := temp
END
END; (* Exchange *)
(* ********************************************** *)

BEGIN (* Main program *)


write ('How Many numbers? : ');
readln (n);
FOR i := 1 TO n DO
BEGIN
write ('x[',i:3, ']= ');
readln (x[i])
END;
exchange;
writeln (' sorted data: ');
FOR i := 1 To n Do
writeln (' x[',i:3,']= ', x[i]:4:1);
readln
END.
12.3 Strings

It has been probably though of an string as an array of type "char". This is true
with two exceptions:

i) Strings use index 0 to store the length byte of the string.

ii) Strings always start "char" date at index 1.

A string variable is declared in the following way

VAR
stringname : STRING[length];

or in the following way

TYPE
stringtype = STRING[length];

VAR
stringname : stringtype;

Notice that when a string variable is declared, it is usually specified the


maximum length that will be needed.

Pascal stores string data a little differently than integers or reals. Strings have
two parts:

i) The number of characters in the string.

ii) The actual characters of the string.

With the storing of integers and reals it is used an analogy of a box inside the
computer.
With the storing of strings, think of a series of adjacent boxes. The first box
stores the number of characters in the string and is called the "length byte". The
remaining boxes store the characters of the string (one character per box).

String variables can hold strings as long as 255 characters. This should be more
than ample for most applications.

Obviously the "length byte" can be computed using function "ord" in the
following way".

ord (stringname [0]);

To obtain the current length of a string it is used function "length":

length (stringname);

This current length consists only of the number of characters in the string. It
does not include the length byte.

According to the above remarks if a string variable is declared as STRING[30],


then it is told to Pascal to use 31 bytes (do not forget the length byte) to store
the string data. If the input string is composed of 5 characters, Pascal still uses
31 bytes. However, the length byte is a "char" value of 5, and the data in
STRING[6] through STRING[30] is undefined.

The following program illustrates the above remarks and how a string can be
treated as an array. The last "writeln" shows how it is usually displayed an
string.
PROGRAM Strarray (input, output);

(* This program illustrates how a string


can be treated as an array *)

TYPE
familyname = STRING[30];

VAR
family : familyname;
i : integer;

BEGIN
write('Enter a string : ');
readln(family);
writeln;
writeln('The length byte is ', ORD(family[0]));
writeln;
FOR i := 1 TO length(family) DO
write(family[i]);
writeln(' ':8, 'This is as an array of char');
writeln;
writeln(family, ' ':8, 'This is as a string variable');
readln;
END.

Note: If it is needed to display a string several times in a program, it is easiest


to declare that string as a constant (CONST) and then display the string
constant name. For instance, if you have to display your company's full legal
name as a constant, such as "compname", and display "compane" to keep from
having to type (and risk mistyping) the entire company name throughout the
program.

As an example of the above note, consider the following program to print a


paycheck.

PROGRAM Paycheck(input, output);


(* Computes and displays a payroll check *)

CONST
space = ' ';
name = 'B A Head';
date = '01/09/93';
taxrate = 0.40;
max = 65;

VAR
i : integer;
hours, rate : real;
grosspay, taxes, netpay : real;

BEGIN
writeln;
write('Input the hours worked: ');
readln(hours);
write('Input the rate per hour: ');
readln(rate);
writeln;
(* Compute the pay *)

grosspay := hours * rate;


taxes := taxrate * grosspay;
netpay := grosspay - taxes;

(* Display the results in the format of a check *)

writeln;
FOR i := 1 TO max DO
write('*');
writeln;
writeln(space : 40, 'Date: ', date);
writeln;
writeln('Pay to the Order of: ', name);
writeln;
writeln('Pay the full amount of: $', netpay : 10:2);
writeln(space : 25,'----------');
writeln;
writeln(space : 40,'-----------------------');
writeln(space : 40,'Dan Chambers, Treasurer');
writeln;
FOR i := 1 TO max DO
write('*');
readln
END.
12.4 String functions

There are several string functions that manipulate strings. It has been
previously seen one of such a functions. That is the "length(x) which returns
the length (the number of characters) of the string variable, constant, or
expression inside its parentheses.

The length-function is useful to test whether a string variable has data (to see
whether the user typed anything before pressing Enter) with the following
section code:

write ('Please type an answer: ');


readln (ans);
IF length (ans) = 0 THEN
writeln (' you did not type anything...');

Another useful function is the "pos-function". This function search for a "char"
or substring within another string. It returns the index of the string where the
substring was found.

The pos-function requires two parameter:

pos (x,y)
where x is the "char" or substring wanted to find.
y is the string wanted to search.

If the "char" or substring is not found, pos-function returns 0.

For example

test := 'this a test string ';


writeln ('The position of "test" is ', pos ('test', test);
writeln ('The position of "is" is ', pos ('is', test));
writeln ('The position of "xyz" is ', pos ('xyz' test));

The preceding statements would display


The position of "test" is 11
The position of "is" is 3
The position of "xyz" is 0

Notice that the second example returns 3 and not 6 pos-function finds the first
match and stops. Because "xyz" is not in the search string, pos-function returns
0 for the last "writeln".

With the copy-function requires three parameters:

copy (x,y,z)

where x is a string variable or a constant string.


y is a constant integer number determining the starting
point of the string.
z is a constant integer number determining how many
characters are returned.

Copy-function can pull any number of characters from anywhere in the string.
The following example shows how the copy-function works:

test := 'Turbo Pascal FORTRAN COBOL C BASIC';


writeln (copy (test, 1, 12));
writeln (copy (test, 14, 7));
writeln (copy (test, 25, 5));
writeln (copy (test, 28, 1));
writeln (copy (test, 30, 5));
This example produces a listing of these five programming languages, one per
line, as shown in the following output:

Turbo Pascal
FORTRAN
COBOL
C
BASIC

Concat-function concatenates two or more strings. The resulting string can


have as many as 255 characters, the maximum string size. For example,

test1 := 'This is only a test. If this ';


test2 := 'had been an actual emergency, you ';
test3 := 'would have been instructed...';
writeln (concat (test1, test2, test3));

The preceding "writelns" would display

This is only a test. If this had been an actual emergency, you would
have been instructed...

Pascal has an alternative form that is much faster to use. If it is required to


concatenate two more strings, use plus signs. For example, the above "writeln"
could be replaced with

writeln (test1 + test2 + test3);


CHAPTER 13

Arrays of arrays = multidimensional arrays

13.1 Some definitions and concepts

In Pascal the concept of array is not limited to one-dimensional arrays; it can


also be defined multidimensional arrays.

A two-dimensional array can be considered as a table of data composed of rows


and columns. The first dimension (i.e. the first index) could refer to the row
number, and the second dimension (The second index) to the column number.

In a similar way, a three-dimensional array could be consider as a set of tables,


something like the pages of a book.

Assume, as an example, that "table" is a two-dimensional array composed of m


rows an n columns. The elements of the array would be

table [1,2] table [1,2] ... table [1,n]


table [2,1] table [2,2] ... table [2,n]
table [3,1] table [3,2] ... table [3,n]
. . .
. . .
. . .
table [m,1] table [m,2] ... table [m,n]

Independently of the array dimension, an array always consists of a sequence of


data of the same type.

A definition of multidimensional array could be included in a variable


declaration. However, for each one of the dimensions must be specified a type.
Consequently, a definition of array could de written as:

VAR
arrayname : ARRAY [indextype1, indextype2,...,
indextype3] OF type

The index type could be simple ordinal types or subranges. It does not have to
be all of them of the same type. On the other hand, the elements of the array
must be always of the same type, including structured types.

As an example, consider the following definition

VAR
table : ARRAY [1..60, 1..150] OF real;

Another way of expressing the same definition is:

TYPE
index1 = 1..60;
index2 = 1..150;

VAR
table: ARRAY [index1, index2] OF real;
Another possibility is:

CONST
limit1 = 60;
limit2 = 150;

TYPE
index1 = 1..limit1;
index2 = 1..limit2;

VAR
table : ARRAY [index1, index2] OF real;

Obviously the first definition is the simplest.

Notice that both indexes in the definition are subranges of integer type, but the
elements of the array are of real type.

13.2 Tables and for-loops

In many applications using multidimensional arrays require the use of nested


loops : a loop for each dimension. Therefore, an application oriented to the
processing of elements of a table could use two loops : one inside the other one.
The outer loop could be used to process each one of the rows, and the inner
loop to process the columns inside of each row.

The above remarks are illustrated in the following program


PROGRAM Table1(input, output);

(* This program reads a table of numbers,


sums the columns of each row and then
sums the rows of each column *)

VAR
row, col : 1..11;
nrows, ncols : 1..10;
table : ARRAY[1..11,1..11] OF real;

(* ************************************ *)

PROCEDURE Sumsrows;

(* Sums the columns of each row *)

BEGIN
FOR row := 1 TO nrows DO
BEGIN
table[row,ncols+1] := 0;
FOR col := 1 TO ncols DO
table[row,ncols+1] := table[row,ncols+1] +
table[row,col]
END
END; (* sum rows *)

(* ************************************ *)
PROCEDURE Sumscols;

(* Sums the rows of each column *)

BEGIN
FOR col := 1 to ncols DO
BEGIN
table[nrows+1,col] := 0;
FOR row := 1 to nrows DO
table[nrows+1,col] := table[nrows+1,col] +
table[row,col]
END
END; (* sum columns *)

(* ************************************ *)

PROCEDURE Readdata;

(* Read elements from a table *)

BEGIN
write('How many rows (1..10)? : ');
readln(nrows);
writeln;
write('How many columns (1..10)? : ');
readln(ncols);
writeln;
FOR row := 1 TO nrows DO
BEGIN
writeln('Enter data for row number ', row:2, ' : ');
FOR col := 1 TO ncols DO
read(table[row,col]);
writeln
END
END; (* Read data *)

(* ************************************ *)
PROCEDURE Results;

(* Write out table and the corresponding sums *)

BEGIN
writeln('Original table, with sum of rows and columns: ');
writeln;
FOR row := 1 TO nrows + 1 DO
BEGIN
FOR col := 1 TO ncols + 1 DO
write(table[row,col]:6:1);
writeln
END
END; (* Results *)

(* ************************************ *)

BEGIN (* Main Program *)


readdata;
sumsrows;
sumscols;
results;
readln;
readln
END.
Some additional declarations for multidimensional arrays are.

TYPE
color = (red, white, yellow, green);
index = 1..100;

VAR
test: ARRAY [color, index] OF boolean

Since "test" is a boolean array, the assigned values to the elements of the array
will be boolean; i.e., they will be "false" or "true".

As it was said before, an array can be of any type, including of structured type.
Therefore it is possible to define a one-dimensional array whose elements be
one-dimensional arrays (all of them of the same type). Such a definition is
equivalent to define a two-dimensional array.

Thus, the definition

VAR
test: ARRAY [1..25] OF ARRAY [1..50] OF char;

is equivalent to

VAR
test : ARRAY [1..25, 1..50] OF char;

You might also like