go to Trig home page   Guide to GAUSS Programming - a basic introduction


 
Program Control

1 Flow of control


Up to now all the code used in the examples and exercises has been presented in a step-by-step way:

instruction1;
instruction2;
instruction3;
...

This section considers how this sequence might be altered to enable more flexible programs to be written.

The approach outlined above is clearly limited. How could reading rows from a dataset be achieved? It would have to be coded explicitly: one instruction for each read command:

mat[1,.] = READR (handle, 1);
mat[2,.] = READR (handle, 1);
mat[3,.] = READR (handle, 1);
...

This is very poor solution indeed. Much better would be to have a loop command. Then all the READRs could be replaced by one call:

LOOP until some condition
mat[currRow, .] = READR (handle, 1);
END LOOP and return to beginning of loop

The loop stops repeating itself when some condition is met. When the condition is met, the program leaps the loop and continues executing after the loop code. Thus there has been a change in the path of the program due to a condition - a conditional branching operation. This would be useful in a general context too - not just to stop loops:

do something
IF some condition is true
do this
otherwise
do that
END branching operation.
do something else

Both the loop and the conditional branch involve changes in the flow of control of the program: the sequence of instructions that the program executes, and the order in which they are executed, is being controlled by other instructions in the program. There are two other ways in which the sequence of instructions can be altered: by the suspension (temporary or permanent) of execution; and by procedure calls:

flow of control
GAUSS also provides the ability for unconditional branching (GOTO, BREAK, CONTINUE) and open subroutines (GOSUB). Use of these is an unconditionally bad idea and so they are not discussed here. Procedures are considered on the next page. This section concentrates on the other controls.

Note that the layout of code segments in this section does not affect the operation of the code; the important bits are the spacing between words and the location of the separating semi-colons.


2 Conditional branching: IF

The syntax of the full IF statement is:

IF condition1;
doSomething1;
ELSEIF condition2;
doSomething2;
ELSEIF condition3;
...
ELSE;
doSomething4;
ENDIF;

but all the ELSEIF and ELSE statements are optional. Thus the simplest IF statement is

IF condition1;
doSomething1;
ENDIF;

Each condition has an associated set of actions (the doSomethings). Each condition is tested in the order in which they appear in the program; if the condition is "true", the set of actions will be carried out. Once the actions associated with that condition have been carried out, and no others, GAUSS will jump to the end of the conditional branch code and continue execution from there. Thus GAUSS will only execute one set of actions at most. If several conditions are "true", then GAUSS will act on the first true condition found and ignore the rest.

IF none of the conditions is met, then no action is taken, unless there is an ELSE part to the statement. The ELSE section has no associated condition; therefore, if GAUSS reaches the ELSE statement it will always execute the ELSE section. To reach the ELSE, GAUSS must have found all other conditions "false". So, ELSE is a catch-all category: it is only called when no other conditions are met, but if the ELSE section is included then some action will always be taken.

ELSE effectively provides a default option, which can be useful in some circumstances:

IF number > 0 ;
numType = "positive";
ELSEIF number < 0;
numType = "negative";
ELSE;
numType = "zero";
ENDIF;
or numType = "zero";
IF number > 0;
numType = "positive";
ELSEIF number < 0 ;
numType = "negative";
ENDIF;

These programs produce identical results, but each might be appropriate in particular cases (if, for example, the default operation was very complex, or there was a need for an initialised variable numType in the branches).

2.1 IF examples

The set of actions may be one instruction, a number of instructions, or even nested IF or loop statements. It could also be a null (empty) statement. For example, augmenting the above code to separate numbers greater than one in absolute terms could be achieved by

numType = "zero";

IF number > 0;

numType = "pos ";
IF number > 1;
numType = numType $+ ">1";
ELSE;
numType = numType $+ "<= 1";
ENDIF;

ELSEIF number < 0;

numType = "neg ";
IF number < -1;
numType = numType $+ ">1";
ELSE;
numType = numType $+ "<= 1";
ENDIF;

ENDIF;

Note the way extra lines and indentation can be used to make code easier to follow. Alternative formulations could be

numType = "zero";
IF number > 1;
numType = "pos >1";
ELSEIF number > 0;
numType = "pos <1";
ELSEIF number < -1;
numType = "neg >1";
ELSEIF number < 0;
numType = "neg <1";
ENDIF;
or IF number == 0;
numType = "zero";
ELSE;
IF number > 0;
numType = "pos ";
ELSE;
numType = "neg ";
ENDIF;
IF ABS(number) > 1;
numType = numType $+ ">1";
ELSE;
numType = numType $+ "<1";
ENDIF;
ENDIF;

In the first form, a number with an absolute value greater than 1 will fit two conditions. The conditions must therefore be ordered properly for the correct set of actions to be taken. In the second case, the ELSEIF option is replaced by a combination of nested IFs and ELSEs.

Finally, as a null statement is still a valid action, these three (for example) are equivalent:

IF condit;
doThings;
ENDIF;
IF condit;
doThings;
ELSE;
;
ENDIF;
IF condit;
doThings;
ELSE;
ENDIF;

3 Loop statements: WHILE/UNTIL and FOR

GAUSS has two types of loops: FOR loops and WHILE/UNTIL loops. FOR loops are used when the number of loops is fixed and known in advance; the others are used when the conditions to enter or exit the loop need to be continually re-evaluated.

3.1 WHILE/UNTIL loops

The format for the WHILE and UNTIL loop statements are

DO WHILE condition;
doSomething;
ENDO;
DO UNTIL condition;
doSomething;
ENDO;

These two are identical except that the first loops until condition is "false", while the second loops until condition is "true". This means that

DO WHILE condition; DO UNTIL (NOT condition);

are identical. UNTIL therefore confuses the issue to no real benefit, and so this section will only use WHILE in its examples. All the code can be converted into UNTIL statements by using the above transformation.

The operation of the WHILE loop is as follows: (i) test the condition; (ii) if "true", carry out the actions in the loop; then return to stage (i) and repeat; (iii) if "false", skip the loop actions and continue execution from the first instruction after the loop.

Note that, first, the condition is tested before the loop is entered; therefore the loop might not be entered at all. Second, there is nothing in the definition of the loop to say how the loop condition is set or altered. It is the programmer's responsibility to ensure that the condition is set properly at each stage.

Two functions, BREAK and CONTINUE, allow the cycle to be interrupted. BREAK exits the loop immediately. CONTINUE takes execution back to the top of the loop where the test condition is re-evaluated. Use of these is generally a bad idea. Use IF statements to ensure an orderly exit from a loop; it makes the program much more traceable.

3.2 WHILE examples

Consider first of all a loop to print the integers 10 down to one. The variable i is used as a count variable:

i = 10;
DO WHILE i /=0;
PRINT i;;
i = i - 1;
ENDO;

Note that the condition is set before entering the loop, and it needs to be updated explicitly, as in the penultimate line. If the line "i = i -1;" was not included, then i would have stayed at 10, the condition would not have been met, and the program would have continued printing out "10" forever. Alternatively, suppose the above code had operated on a user-entered number:

PRINT "Enter start number ";;
i = CON (1, 1);
DO WHILE i /=0;
PRINT i;;
i = i - 1;
ENDO;

If the user enters a negative number to start, then i will never equal zero. Eventually the program will crash when i gets to -5.0E305, although this could take some days and an observant programmer may suspect that something has gone wrong before then. In this case the problem is easily avoided by changing the third line to

DO WHILE i > 0;

If the user enters a negative number with this condition, then the loop will not be executed at all.

Because the condition is tested at the beginning of a loop, the place at which the condition is changed will affect the outcome. Consider a variation on the above code:

i = 11;
DO WHILE i /= 1;
i = i -1;
PRINT i;;
ENDO;

This will have exactly the same result, but in the second case the condition is being changed before any action takes place, which necessitates a slight variation on the loop test and the order of instructions within the loop.

3.3 FOR loops

A FOR loop cycles a fixed number of times. In this it differs from the WHILE loop, which checks the end condition on every cycle. Because of this, the FOR loop operates much more quickly. For example, adding up all the integers from 1 one to ten million takes (on my computer) about fifteen seconds for the WHILE loop and six for the FOR loop.

The format of the FOR loop is

FOR i(start, stop, interval);
:
ENDFOR;

The variables start, stop, and interval control the number of times the loop operates. The loop will count from start to stop in steps of interval. Unlike the WHILE loop, there is no need to reset the counter explicitly.

The counter i can be referenced within the loop, but should not be changed. The counter is also local to the loop. That is, if not pre-existing when the loop starts it is created; it is controlled by the loop properties; and on exiting the loop (whether normally or through BREAK) the value is undefined.

3.4 FOR examples

To rewrite the above code to count down to zero using a FOR loop, then

FOR i (10, 1, -1);
PRINT i;;
ENDFOR;

This is much more compact then the corresponding WHILE loop, and will operate faster too.

The loop parameters can also be variables:

PRINT "Enter start number ";;
i = CON (1, 1);
FOR j(i, 1, -1);
PRINT j;;
ENDFOR;

Note that the start condition does not have to be explicitly tested. If the user enters a number less than 1, then the loop will not be entered at all.


4 Suspending execution: PAUSE, WAIT and END

All these commands stop execution either temporarily or permanently. In addition, some key combinations may stop a program in an emergency.

4.1 Temporary suspension using commands

Three commands can lead to the temporary suspension of a program:

PAUSE (sec);
WAIT;
WAITC;

PAUSE will wait for sec seconds before the program continues. WAIT will wait until a key has been pressed. However, because a user may type ahead of the computer, WAITC will clear the keyboard buffer before waiting for a key, so that the program will always stop long enough for, for example, a message to be read. In this, WAITC works much the same as the MS-DOS "pause" command.

These functions are most useful where the program is stopped while something is being checked or a message is displayed which should be read. For example, trying to open a file on the floppy disk drive "a:" may fail if there is no disk in the drive. To try to prevent this, a piece of code could be included in the program:

PRINT "Looking for a:\x.dat. Please ensure drive a: is ready. ";;
PRINT "Press any key to continue";
WAITC;
OPEN handle= "a:\x.dat" FOR READ VARINDXI;
...

WAIT and WAITC cannot be used to read console input. The key read by either of these two is lost to the program. The key is only wanted for its signalling role, not for its inherent value, and GAUSS throws the key away once the signal has been received.

Note that these commands work differently under Unix because of the way Unix handles input streams. Often a carriage return is required. The particular result depends on your system and the form of GAUSS you use.

4.2 Terminating a program using commands

When GAUSS has finished executing all the instructions in a file, the program is finished. However, GAUSS just returns to command mode; all the parameters, environment settings and variables used by the program still exist and are accessible to either instructions on the command line or new programs. This is the main reason for calling NEW at the beginning of a program: it clears out all the rubbish from any previous work.

Having variables around is not a problem. GAUSS could run out of memory, but as the program is finished this is unlikely to be a serious problem. However, the case for file access is different. Many PCs, and GAUSS, have some sort of disk cacheing system: a small, fast bit of memory is used as an intermediary store between disk and "normal" memory to avoid excess disk accesses. If a GAUSS dataset has been used for writing, then the last set of changes may not be permanently written to disk until the file is CLOSEd. Closing a file is the only way to be sure (relatively) that updates are properly written to disk. The GAUSS manual is silent on what happens to open files when the GAUSS environment is left. Therefore, in a worst case, running a program and then leaving the GAUSS system could result in some data being lost even though the program has run "correctly".

Other reasons for closing files were advanced in the I/O section. As well as data files, a program may terminate with a variety of screen on/off and output on/off settings. This may be confusing, and could lead to spurious entries in the output file or a failure to carry out display instructions in other programs.

Ideally, a program should close all files and reset all screen and output options before it terminates. However, the command

END;

will also carry out these functions. END tells GAUSS that the program is complete. Even if there are more instructions, the program will terminate at this point. Moreover, the housekeeping functions will ensure that there is an orderly exit from the program. Neither NEW or END is necessary to a program, but between them they increase the security of the program and the integrity of the GAUSS environment. If several programs are being run, they will also improve efficiency of the programs by keeping the workspace tidy.

END can be placed anywhere in a program. Whenever it is encountered, the program stops. However, ENDs in the middle of a program are rarely a good idea. Having multiple exit points from a program confuses the issue, usually unnecessarily.

An alternative to END is

STOP;

This also indicates to GAUSS that execution is finished, but none of the housekeeping tasks are carried out. This could be used where, for example, a program had to be stopped in an emergency with files left open for examination. It is of little practical use. Use END in preference.

In GAUSS 5.0, END causes what appears to be an error message to come up at the end of the program. This can be ignored.

[ previous page ] [ next page ]