0% found this document useful (0 votes)
4 views7 pages

Programs

The document discusses best practices for writing structured programs in the SPARKS language, emphasizing readability and proper use of iteration statements. It outlines guidelines for defining procedures, using loops, and the differences between functions and subroutines, as well as the importance of recursion. Additionally, it provides examples of recursive procedures and their efficiency in computing mathematical functions.

Uploaded by

vidhatrisharma05
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views7 pages

Programs

The document discusses best practices for writing structured programs in the SPARKS language, emphasizing readability and proper use of iteration statements. It outlines guidelines for defining procedures, using loops, and the differences between functions and subroutines, as well as the importance of recursion. Additionally, it provides examples of recursive procedures and their efficiency in computing mathematical functions.

Uploaded by

vidhatrisharma05
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

WRITING STRUCTURED PROGRAMS

Since most of the SPARKS programs will be read many more times than

they will be executed, we have tried to make our code readable. This is a

goal which should be aimed at by everyone who writes programs. The SPARKS

language is rich enough so that one can create a good looking program by

applying some simple rules of style. Some of the commonly accepted rules

of style are:

(i)

Every procedure should carefully specify its input and output variables.

(ii)

(iii)

(iv)

(v)

(vi)

The meaning of all local variables should be defined.

The flow of the program should generally be forward except for

normal looping or unavoidable instances.

Indentation rules should be established and fol!owed so that com

putational units of program text can more easily be identified.

Documentation should be short, but meaningful. Avoid comments

like "i is increased by one."

Use subroutines where appropriate.

See the book The Elements of Programming Style by Kernighan and Plauger

(McGraw-Hill) for more examples of good rules of programming.

Which iteration statement to use

Since SPARKS contains four different ways for getting a set of statements

to be repeatedly executed, it is natural to ask under what circumstances

we prefer to use one way rather than another. The four iteration statements

are the (i) loop-repeat, (ii) while-repeat, (iii) loop-until-repeat, and (iv)

for-repeat. Suppose we want to read a set of values until their sum exceeds

a predefined limit, say n. This would naturally be expressed using the while
loop as:

y-0

while y ::5 n do

read(x)

y -y + x

repeat

Wridng structured programs

15

On the other hand suppose we want to read in n values and process each

one in some way. Using the while loop we might write:

i-1

while i :s n do

read(x)

call PROCESS(x)

i-i+1

repeat

But in this case it is preferable to employ a for loop, as for example

for i - 1 to n do

read(x)

callPROCESS(x)

repeat

The reason for favoring the for loop in this context is not so much because

we save two statements (i - 1 and i - i + 1) but because the number of

iterations was fixed by n and independent of the data being read. When

we know exactly how many times we want to iterate a group of statements

then the for statement should be used. When we want to iterate some state

ments until a certain condition becomes true or false then the while loop is

favored.

Now suppose we want to read a set of values and process them until we

read an end-of-file marker. Using the while loop we can express this as:

read(x)
while x #- eof do

call PROCESS(x)

read(x)

repeat

However a better way would be using the loop-repeat:

loop

read(x)

if x = eof then exit endif

call PROCESS(x)

repeat

16

Introducdon

Now we have only one occurrence of the read statement which may prove

useful if we have to modify this program segment. In fact suppose we now

want to modify this program to test the result of call PROCESS(x), say

PROCESS(xJI) and terminate processing if y = 0. Then the loop-until

repeat can be fruitfully used:

loop

read(x)

if x = eof then exit endif

call PROCESS(x,y)

until y = 0 repeat

if x #- eofthen ...

The Case for CASE

There are two statements in SP ARKS for discriminating between alter

natives: the if-then-else and the case. We could have gotten by with either

one of these statements since we can simulate the case, as defined in Figure

1.3, using the nested sequence of if-then-else's

if condl then SJ

else if cond2 then S2

else if condJ then SJ


else if condn then Sn

else Sn + 1

endif endif . . . endif

The virtue of the case is that it eliminates the nesting of alternatives and

brings the conditions out to the same level. The amount of syntax is reduced

if many conditions are involved and the resulting program segment is easier

to read.

Functions versus Subroutines

Most programming languages including SP ARKS permit the definition

of both functions and subroutines. But very few language primers discuss

when one is preferable to the other. Before we can get close to answering

this question, let us first reconsider the way variables get used in a proce

dure. In section 1.2 we noted that variables could be classified as either

local, global, or parameter. There is another three category classification

that pertains only to parameters and global variables. In one instance a

variable may carry a value in to a procedure, but it remains unchanged ------------------~-----~----

Writing structured programs

17

throughout execution. A second possibility is that a variable is undefined

upon entry but it is assigned a value which is carried out when the proce

dure ends. The third possibility is a variable which both brings a value in

and (a possibly changed value) out. A language designer might even go

so far as to insist that a programmer declare his variables in this way, as

to whether they are in, out, or inout as this adds another measure of relia

bility. However we will refrain from adding this declaration feature to

SPARKS, at least for now.

The reason for introducing these categories is to help us understand the

notion of side effect. The words subroutine or pure procedure are used to

denote a procedure which returns no function value but may alter either its

parameters or global variables or both. A procedure which does alter one

of its parameters or global variables is said to have a side effect. This is


equivalent to a procedure which has at least an out or inout type of variable.

Pure procedures work solely through side effects. A procedure which is a

function may also have side effects. But in the interests of reliability SPARKS

insists that one uses either functions without side effects or pure proce

dures.

In general we write a function when the value it returns will be used once

in an expression. For example if we needed a procedure to determine

whether two trees are equal we should create a function, say EQUAL(S, T),

which returns either true or false (a Boolean function). Then in a program

we could say

[Link](S,T) then ...

Or we might create a function for computing the greatest common divisor

and use it in the assignment

z - x *ylgcd(x,y)

However if we need the gcd(x,y) more than once we can either assign its

value to a variable (t - gcd(x,y)) or we can make it a subroutine with a

side effect (callgcd(x,y,t)).

Recursion

Recursion is a powerful programming technique which unfortunately is

not employed to the extent it should. There are at least two reasons for this.

One is the fact that FORTRAN does not permit recursion. Thousands of

people who have learned the art of programming using FORTRAN have

thus been unable to experience its benefits. Two is the fact that there is

18

Introducdon

often a heavy penalty in terms of execution time when one uses recursion

on some compilers. We shall be running some experiments later to see if

we can quantify this penalty.

Let's take a look at some examples, both good and bad, which make use

of recursion.

Example 1.1 The Fibonacci sequence l,1,2,3,S,8,l3,21,34, ... is defined


as

Fo = 0, Fi = 1, F; = F;-1 + F;-2, i >

This mathematical definition might naturally lead to the recursive SPARKS

procedure:

procedure F(n)

I !returns the nth Fibonacci number! I

integern

ifn :s 1 thenretum(n)

else retum(F(n - 1) + F(n - 2))

endif

endF

Algorithm 1.2 Fibonacci numbers

The virtue of this program is that it is almost syntactically identical to

the mathematical definition. However it is atrociously inefficient from the

standpoint of computing time. But the major source of the inefficiency

does not arise because recursion is used. Rather it is because of the way

the computation proceeds. Many values are recomputed many times; for

example F(n - 2) is computed twice, F(n - 3) is computed three times,

and F(n - 4) is computed five times. Other recursive versions can be con

structed which are far more efficient (see the exercises).

Example 1.2 Perhaps the oldest recorded nontrivial algorithm is due to

Euclid. This algorithm is for computing the greatest common divisor of

two nonnegative integers. The essential step which guarantees the validity

of his method consists of showing that the greatest common divisor of a and

b (a > b =:::: 0) is equal to a if b is zero and is equal to the greatest common

divisor of band the remainder of a divided by b if bis nonzero. For exam

ple:

Writing structured programs

gcd(22,8) = gcd(8,6) = gcd(6,2) = gcd(2,0) = 2

and

gcd(21,13) = gcd(13,8) = gcd(S,S) = gcd(S,3) = gcd(3,2) = gcd(2,1)


gcd(l,0) = 1

Expressing this process as a recursive procedure one gets

procedure GCD(a,b)

I I assume a > b <::: 0/ I

if b = 0 then retum(a)

else retum(GCD(b, a mod b))

endif

endGCD

Algorithm 1.3 Greatest common divisor

19

Example 1.3 One often gets the mistaken impression that recursion is

only appropriate for computing "mathematical" functions. Here is a pro

cedure which searches for x in A(l:n).

procedure SEARCH(i)

I /if there exists an index k such that A (k) = x in A (i: n )/I

I !then the first such k is returned else zero is returned.I I

global n,x,A(l:n)

case

: i > n : retum(O)

: A (i) = x : retum(i)

: else : retum(SEARCH (i + 1))

end case

end SEARCH

You might also like