Flow of control

Flow diagrams

Talking about flow of control is so much easier with pictures to help us out that a standard set of conventions for use in flow diagrams or flow charts has emerged. In fact, we surreptitiously used a number of these conventions in our first representation of the path through double.space. Boxes with rounded corners were used to mark start and stop states. Rectangular boxes - the stepping stones between start and stop - contained instructions to perform actions. Arrow heads indicated the direction of flow or movement through the network. All that was missing, and is now needed to cope with the behaviour of an if, is a way to represent the branching of a path and the test which leads to the selection of one branch rather than the another. Since a test is something which is either true or not (is successful or not), we clearly need a box which has one way in and two ways - suitably distinguished - leading out. The standard flow-diagram way to draw this is to use a lozenge to house the test with its exits marked true and false:

The two input version of if

Take a look now at the two input form of if which we schematized earlier as:

if test [what.to.do.if.true]

In interpreting this variant, LOGO carries out the instruction(s) in the second input list, [what.to.do.if.true], when the condition is met. But if the test proves false, LOGO does nothing.

Now let us suppose that, within the body of a procedure definition, an if instruction like this is followed by another instruction, which we will call simply what.to.do.next, since what it actually does is unimportant. Using the conventions we have just described, the portion of a flow chart, corresponding to

if test [what.to.do.if.true]

would look like this:

You can see that the instructions in what.to.do.next are always carried out, but there are two distinct routes to get to that point, one indirect (when test outputs "true) and one direct (when the test outputs "false).
In practice, the lines of a flow chart like this would normally be simplified (on the understanding that the flow along the lines is unidirectional). Let's do that:

The three input version of if

If we use the three input form of if in the same context

if test [what.to.do.if.true] [what.to.do.if.false]

then the instructions in what.to.do.next are still necessarily followed. But the presence of [what.to.do.if.false], which identifies the instructions which LOGO must perform if test outputs "false, ensures that there is no longer a direct route to what.to.do.next. The two instruction lines work together instead to create the following rather different looking network:.

You will see that there are still two routes to what.to.do.next, but this time both pass through intermediate steps, one when the test outputs "true and a different one when it outputs "false.

Reaching a crossroads

Although if only acts on receiving a "true or a "false, you can always handle what you might think of as multiple branches from some single state by using a series of two-input ifs to identify the various conditions which determine the path taken. For example, suppose that in some situation it is possible to apply three different tests, test.a, test.b and test.c (Walking north towards a real crossroads, you might ask yourself: Do I continue to go north, or turn east, or west?) Suppose, additionly, that one and only one of the three tests, can be true. (You couldn't go three ways at once after all.) In these circumstances, the following sequence of schematic instructions lines

If test.a [what.to.do.if.a.is.true]
If test.b [what.to.do.if.b.is.true]
If test.c [what.to.do.if.c.is.true]

create a structure which you might think of as representing the crossroads:

But notice that writing a definition which includes in this way a series of ifs does not, of itself, guarantee that one and only one of the conditions will be met and that the flow represented informally in diagram above is achieved. Whether or not a condition is fulfilled is determined by the evidence available and knowing in advance what that evidence might be on any occasion is not always easy for the programmer. For that reason it is preferable, even if less succinct, to identify fully the relationships which are concealed in the crossroads diagram using the more normal flow chart conventions.

At a crossroads you are in fact faced by a series of yes-no decisions. Are the conditions right (or not) for taking the path to the north? Are the conditions right (or not) for taking the path to the west? And so on. When we spell this out in a fuller diagram, we can see more readily what would happen if, in some particular case, either more than one of the tests or none of the tests were met.

This diagram shows clearly that when none of the tests - test.a, test.b or test.c - are met none of the instruction making up the lists [what.to.do.if.a.is.true], [what.to.do.if.b.is.true], [what.to.do.if.c.is.true] are carried out. (Only the routes marked false are taken through the network.) At the other extreme, when all of test.a, test.band test.c output "true, then all the instruction in all of the lists [what.to.do.if.a.is.true], [what.to.do.if.b.is.true], [what.to.do.if.c.is.true] are carried out. (Every true route is taken and each true leads to an instruction box.). And between these poles any combination of a, b, and c might turn out to be true with consequences that are easily traced using the chart.

Getting off at the first stop

Flow diagrams provide useful general purpose notations to deal with any pattern of events, not merely the steps in a computer program. But given that, for our purposes, these diagrams are intended specifically to picture the sequence of events which take place when a LOGO procedure is invoked, we should consider one further possibility particular to this situation. If, as we said earlier, the instruction lists which act as the second and third inputs to an if command are made up of just any ordinary LOGO instructions, then any (or all) of the instruction lists [what.to.do.if.a.is.true], [what.to.do.if.b.is.true], [what.to.do.if.c.is.true] could end with op "something.or.other or stop or toplevel, all of which would terminate execution of the procedure containing the if command.

Suppose, for example, that the four lines

If test.a [what.to.do.if.a.is.true]
If test.b [what.to.do.if.b.is.true]
If test.c [what.to.do.if.c.is.true]

form part of the body of an operation (i.e. a procedure which outputs. Suppose also that [what.to.do.if.a.is.true] includes as the last (and possibly only) instruction in the list op "something.or.other. Suppose by contrast that this is not the case with [what.to.do.if.b.is.true] and [what.to.do.if.c.is.true]. Now remember that one of the effects of op is to shut down the procedure it appears in. In order, therefore, to represent the corresponding control regime we need a modification of the previous diagram which includez a stop box .

In this situation, as soon as [what.to.do.if.a.is.true] is carried out, the game is over. The rest of the tests and instructions in the procedure are completely ignored. The output - and control of events - is passed back immediately to the caller of the procedure.

Since op (alternatively stop or toplevel if the parent procedure was a command) could have been included in any of the 3 instruction lists, a series of ifs clearly provides considerable opportunity for varying the flow of control within a procedure.

If you are really impatient to put it all together and build conditional instructions into a procedure, click Next page.

Ron Brasington
Department of Linguistic Science
The University of Reading

E-mail: ron.brasington@rdg.ac.uk