p

October 30, 2001 ­c Achim Jung and Uday Reddy Handout 7 Loops

1. Using branching to save code. On Handout 5 we saw how by giving the processor control over its program counter we can implement alternative execution paths:

flow diagram: machine code: condition

branch if positive no yes

condition

C

½

C C ¾ statement ½ statement

branch unconditionally

C ¾

By using two branching instructions we can make sure that although machine instructions are lined up linearly, only one

C C ¾ of the regions ½ and is executed. From this example, it is easy to see how any flow diagram could be translated into machine code. Branching can also be used to avoid a repetition of code, as in the following examples:

infinite loop: while - do loop:

loop body loop body

condition

branch if positive

2. Spaghetti code. In a sense, however, branching is too powerful, because it allows us to write programs which are very intricate and hard to understand. Here is an example:

region 1

region 2

The top half looks like an ordinary loop but then from the end of region 2 the program jumps back into the middle of the body of that loop. There is nothing in the design of machine language to preclude such programs. Similarly, the language of flow diagrams also allows arbitrary jumping from one place to another. One speaks of spaghetti code for this “style” of programming. Early programming languages were very close to actual machine code. and BASIC, for example, retain the concept of a numbered list of instructions with jumps from one location to another as the primary control construct. These

1 pg Although later languages (such as ALGOL, PASCAL, C/C++/JAVA) retain some form of , its use is now very much discouraged, and widely considered to be unnecessary. In the remainder of this handout I will try to explain why. 3. . The drive to oust goto began with a landmark paper by Edsger Dijkstra, one of the most influential people in the development of Computer Science. It is called GOTO statement considered harmful and was published in the Communications of the ACM in 1968. A huge debate began which included most eminent computer scientists of the time. The debate is now widely considered to be resolved, with Dijkstra and his concept of structured programming generally being accepted. Structured programming controls the execution flow through a small number of well-defined constructs, none of which include goto. You already know some of these constructs: Branching This class refers to alternative execution paths. In Java the constructs are called if else and switch. Iteration This refers to loops of various shapes. In Java we have three flavours of loops, for, while,anddo - while. Java’s “methods” are to to be mentioned here. I will describe the underlying mechanisms on the next handout. Directives This refers to exceptions and breaks. In this handout we concentrate on iterative constructs, that is, on loops. 4. The (idealised) for-loop. The simplest idea to create a loop is to introduce a counter and to iterate for a fixed number of times. Java offers for this purpose the for-loop, which, when used as a counting loop, will take form:

for ( ; ; ) statement;

Other languages have a more suggestive notation:

for counter = step until do statement;

From a conceptual point of view, the important feature of an idealised for-loop is that as soon as it is entered, it is known how many iterations will be performed. Thus, a for-loop is a safe construct; it can not lead to non-termination. Furthermore, in an idealised for-loop the control variable only exists for the time the loop is active and is only available for reading. Neither should one need to declare it (since it is part of the construct) nor should it exist after the loop has finished. The for-loop in Java does not strictly adhere to these principles. Its effect in terms of machine language is that the various parts of the declaration

for (A; B; C) D;

are compiled separately and assembled in the order

A start: B Branch if false to continue D C Branch to start continue:

Whether these parts set up a proper counter-controlled loop is not checked. The parts A, C, and D can be arbitrary statements, B can be an arbitrary condition. 5. The while-loops. The idealised for-loop is not sufficient for all programming purposes. There are a number of short-

comings: Æ (a) It can be shown that there are numerical functions (that is, functions from Æ to ) which cannot be programmed using for-loops only. You will hear more about this in the second year module on Models of Computation.

2 gyp For example, in a computation with real (= floating point) numbers you may want to repeat an approximation process until your result is stable up to a certain precision. How many iterations are needed for this may be difficult to determine in advance. (c) There are situations when you repeatedly have to deal with input from some other process (or the keyboard) which is not under your control. Then the number of repetitions is unpredictable by principle. You may even have to repeat forever. A more general programming construct than the for-loop is given by the while-loop. In Java the syntax is

while (condition) statement;

Its semantics in (meta-)assembly code and as a flowchart is as follows:

start: no yes EVALUATE condition condition BRANCH_IF_FALSE cont EXECUTE statement BRANCH start statement cont: ...

As is the case for the for-loop, the body of a while-loop may not be executed at all because the test is made before the body is entered. The difference is that a while-loop does not provide an automatic or standard way of ensuring that the loop is ever terminated — it can run forever. 6. Evaluation of Java’s loop constructs. Let us step back and try to evaluate whether Java’s three loop constructs constitute a good choice. Minimality. It is a good thing to have fewer rather than more constructs in a programming language, because a smaller language means that a programmer needs to get familiar with fewer concepts. In my opinion, Java strikes a good balance in this respect, although its set of constructs is not minimal at all, as the following theorem by B¨ohm and Jacobini illustrates: Theorem Every flow diagram can be implemented with if-then-else and while only. The proof is not hard at all and you may want to think about it yourself. Naturality. This is the question whether naturally occurring programming tasks have a direct implementation in Java,or whether some kind of trickery is necessary in certain situation. Let’s look at three possible extensions: Often, an iteration is for a fixed number of rounds only but a counter is not explicitly needed in the body of the loop. For such cases, it would be nice to have a construct of the form: repeat times ; To be forced in such situations to introduce a counter explicitly and to make the right choices about initial value and final value seems distracting. Another often cited example is the problem of reading from a stream. Here we are reading items from some data source until the end of the stream is indicated. The natural flow diagram for such a task seems to be

read first data item

end + of file?

-

read next data item

3 )y g p p accepting a certain amount of code repetition: read-first-data-item; while (! end-of-stream) { process-data-item; read-next-data-item; } Finally, it may well be that a loop is controlled by two quite independent conditions. The prime example is searching an array for the first occurrence of a certain value. Such a search should be terminated if either the value is found or the end of the array is reached. The natural flow diagram for this task is

setindexto0

end + of array?

-

item + found?

-

advance index

where we see that we should have the facility of two exit points in the loop. This is quite a tough problem (although one can get around it in practice, as the Theorem by B¨ohm and Jacobini shows.1) Another eminent computer scientist, Donald Knuth, has written a whole paper on it: Structured programming with Go To, Computing Surveys 6, pp. 261– 302.

Homework Exercise Sheet 4

Homework exercises (Submit via the correct pigeon hole before next Tuesday, ), 8pm.

1. Write a program in assembly code which finds the largest of three integer values stored in locations a, b,andc and stores the result in location d. (Hint: It will help you to draw a flow diagram first.) 2.GivenanarrayM of size 200, which contains marks of students in a course, design a program fragment to calculate the average mark of all the students. The design should be in the form of Java pseudo-code, not assembly code. 3. Write an assembly program (fragment) which checks whether the value 7 occurs in an array A of integers of length 100. If the value occurs at least once then the program should store the value 1 in an integer location called FOUND.Otherwise,it should store 0 in that location. 4. The Sieve of Eratosthenes is a method to find prime numbers (which are numbers divisible only by 1 and themselves). One writes down all number from 1 to some upper limit (100, say) and then one successively crosses out the multiples of 2, the multiples of 3, the multiples of 4, etc, all the way up to 100. Those numbers which have not been crossed out are prime (except 1, which traditionally is not considered to be prime). (a) Perform this procedure by hand to find all prime numbers below 20. (b) Using for-loops write a pseudo-code program (fragment) which implements the Sieve of Eratosthenes to find the prime numbers below 10000. (c) (If you haven’t had enough) Optimise your program with respect to the number of “crossings-out” performed.

1Also, this seems to be a situation where the case for Java’s break could be made.

4