Into the ARC

Mike Williams discusses the use of functions and procedures in Basic V.

Last month, in the first of this new series, I took a broad view of the whole process of program development. If your acquaintance with programming, particularly using BBC Basic on the Archimedes is rather limited, then you maybe felt that I left too many questions unanswered. Well, I only have so much space in the magazine, so this month I propose to look in more detail at some of those features which I had to refer to briefly last time.

Foremost among the better things of BBC Basic are functions and procedures. If you are going to follow the structured approach to your programming which I advocated last time, then you need to use procedures and functions from the word go. To some this may seem like trying to run before you can walk, but I firmly believe that if you get the bones of your program right, filling in the detail becomes a lot easier.

Functions and procedures both work on the same principle. We identify some small routine or task within a program, we package it up with a unique name, and then whenever we need to use that routine we just refer to it by the name previously bestowed. Putting it more technically, we need to define a function or procedure before it can be called. However, provided the definition of the function or procedure exists somewhere in the program, Basic is not that fussy as to where it is located. So whether the definition or the call appears nearer the start of the program doesn't matter.

Traditionally, in BBC Basic, procedure and function definitions tend to form the latter part of the program, and calls to these routines usually appear earlier. When a procedure or function is first called, Basic searches through the program till it finds the appropriate definition. It then enters the name of the procedure or function in a table so that the next time the same routine is called it can be found much more quickly by looking up its name in the table.

And what, you may be wondering, is the difference between a function and a procedure. It's quite simple: a function always returns an answer or result, whereas a procedure performs a task which may or may not produce any 'results'. A user-defined function is like any other in Basic, such as SIN or ABS for example, and may be used similarly. A procedure is more like an instruction (for example, PRINT), and is used in similar situations, though it doesn't have to have anything to do with printing of course.

To define a procedure we must write:
DEF PROCname
.................
<body of procedure>
.................
ENDPROC

but you can give your procedures any names which you choose (using the characters 0-9, A-Z, a-z, '£' and 'underline'), and it usually helps to give meaningful names that remind you of the procedure's purpose. Typically in a program you might see such procedures as:
PROCset_upPROCtitlePROCmenuPROCselectPROCreport1etc.
The so-called body of the procedure simply consists of whatever instructions are needed to perform the task in question. We might have a procedure definition such as:
1000 DEF PROCset_variables
1010 x1=0:x2=640:y1=0:y2=512
1020 exit=FALSE:repeat=TRUE
1030 ENDPROC

Look at the procedures used in programs published in RISC User. These will give you plenty of examples to study and follow, and you may find some that can be adapted to your own programs. A procedure is called by simply writing its name (preceded by the four letters PROC).For example, the procedure defined above would probably be called once near the start of the program such as:
150 PROCset_variables

Now the procedure PROCset_variables is completely self-contained, requiring no other information from the outside world (i.e. from the rest of the program). That's fine, but often when a procedure is called, we need to pass it some additional information. For example, it can be very useful to have a procedure which will display a text message at any point on the screen, and in a colour of our choice. For the procedure to work it will need to know the x and y co-ordinates of the screen position, the text to be displayed, and the colour to use. Such pieces of information passed to a procedure are called parameters, and are included in the procedure definition.

Thus we might write:
1100 DEF PROC display_text(x,y,msg$,c)
1110 COLOUR c
1120 PRINTTAB(x,y)msg$;
1130 ENDPROC

When the procedure is called, we specify the parameters we want. So, for example:
PROCdisplay_text(5,1,"Hello world",6)
would display the message "Hello world" in cyan (colour 6 in any 16 colour mode) starting in position 5 on line 1. You can try this out directly, once you have type in the procedure definition, to test that it works.

The parameters '5', '1', etc. are called the actual parameters, because they are the ones really used, whereas the parameters 'x', 'y', etc. used in the definition are called the formal parameters because they exist only to allow us to define formally the procedure in the first place. Formal parameters can be thought of as variables that only exist inside the procedure definition. Furthermore, Basic never confuses the use of 'x' say as a formal parameter, and the use of 'x' as a variable anywhere else in the program. In effect, a formal parameter is local to a procedure.

Now let's consider another example, this time a procedure to get the name of a month of the year given the month number, and print it out. Here is the procedure definition:
1200 DEF PROCget_month(month_no)
1210 FOR I%=1 TO month_no
1220 READ month$
1230 NEXT I%
1240 PRINT month$
1250 ENDPROC
1260 DATA January, February, March
1270 DATA April, May, June, July
1280 DATA August, September, October
1290 DATA November, December

To print out the name of the sixth month of the year, for example, we could write:
110 PROCget_month(6)
The procedure has one parameter, the number of the required month. In addition, the procedure needs to use an extra variable (I%) to control the FOR-NEXT loop, and a variable month$ to temporarily store the name of a month. When writing many procedures in a program, it can be difficult to remember whether any such particular variable is already in use for another purpose. We can avoid any potential problems by declaring any extra variables used in this way as being LOCAL to the procedure. This means that Basic will correctly distinguish between any variable declared in this way, and the same variable name used elsewhere in the program. We will do this for PROCget_month above by adding:
1205 LOCAL I%,month$
If that line were included, Basic would remember any value assigned to I% on entry to the procedure, and restore it on exit.

You can easily test this. Take the modified procedure above and add:
100 I%=-999
110 PROCget_month(6)
120 PRINT I%
130 END

to form a complete program. Run this and you will see the value of I% printed as -999 even though the procedure has used I% for quite a different purpose.

It is always sensible to declare as LOCAL all variables (and arrays, which we'll deal with on another occasion) which are introduced and used only within the definition of a procedure. If you want to send any information to a procedure, do so as a parameter. That is the 'good' approach, and with longer, more complex programs it does pay dividends.

Many programmers are quite casual about the use of LOCAL, frequently omitting it unless absolutely essential, and often their programs still work. Trouble is more likely to arise when modifications are made to a program that has been in existence for some time. If you have not used LOCAL, it is only too easy to inadvertently pick on a variable to use in a procedure definition that has some quite different purpose elsewhere in the program. If clashes do occur, the results can be very unpredictable, and difficult to trace.

So the general rule is to use parameters to pass information to a procedure and to use LOCAL for everything within the procedure. However, with care we can be a little more relaxed than this, with some advantages as we shall see in the examples which follow.

In the case of the earlier procedure to print the name of a month given the number of the month, it seemed natural to use the month number as a parameter. In other examples it can seem just as natural to refer directly to some variable.

Here is a short procedure to read in a number of data items (the number is the parameter of the procedure), and to store these in a list or array. This is referred to directly, and is assumed to have been dimensioned earlier in the program.
500 DEF PROCread_data(n)
510 LOCAL I%
520 FOR I%=1 TO n
530 INPUT"Next value: " data(I%)
540 NEXT I%
550 ENDPROC

The procedure executes a loop to read in one value at a time, and store this in the array data(). The loop variable I% is only relevant inside the procedure so it has been declared as LOCAL.

All that I have said so far can be equally applied to functions. In addition, a function always returns at least one value. The definition of a function takes the form:
DEF FNname
................
<body of function>
................
=<value>

The name is preceded by the letters 'FN', and the definition terminates with an equals sign followed by an assigned value, but otherwise it follows very much the procedure model.

Suppose we now want to analyse the data we have read in to find the maximum, minimum and average of the stored numbers. A function is the ideal choice now:
600 DEF FNmax(n)
610 LOCAL I%,max:max=data(1)
620 FOR I%=2 TO n
630 IF data(I%)>max THEN max=data(I%)
640 NEXT I%
650 =max

This one finds the maximum value in the array. It sets the (temporary) maximum to be the first value in the array, and then compares this with all subsequent values, replacing it if a larger value is found. The last line of the definition returns that maximum value.

To see how the function is used, we could add some additional instructions to the definitions of PROCread_data and FNmax above to form a complete program:
100 MODE 12
110 INPUT"How many values": " n
120 PROCread-data(n)
130 PRINT"The maximum value is ";FNmax(n)

Since the function returns a value, it can be used anywhere a value is used. Here it is included directly in a PRINT statement. You may like to try writing further functions to similarly find the minimum and average of the values read in.

Functions, and particularly procedures, should form the background of any well-written program. Don't be afraid of using them, and as often as you like. Although you may feel that their main value is in avoiding the unnecessary repetition of the same sequence of instructions, you will find many examples where a procedure is called just once. Using procedures allows larger programs to be divided up into smaller and more manageable 'bites'. With this approach you can concentrate on getting each individual routine correct before moving on to the next.