RISCOS.com

www.riscos.com Technical Support:
BBC BASIC Reference Manual

 


Procedures and functions


Procedures (PROCs) and functions (FNs) provide a way of structuring a program by grouping statements together and referring to them by a single name. The statements can be executed from elsewhere in the program simply by specifying the procedure or function name. A function returns a value, but a procedure does not.

The two structures are very similar, but they are used in slightly different circumstances. PROCs are used wherever a statement can be executed. FNs are used in expressions, wherever a built-in function might be used. Whereas procedures end with an ENDPROC statement, functions return using =expression. The expression is returned as the result of the function call. Functions can return integers, floating point numbers or strings.

Defining and calling procedures

Procedure names begin with the keyword PROC, followed by a name. The following shows how a procedure may be defined and called:

 10 MODE 12
 20 PRINT TAB(0,10)"Countdown commencing ";
 30 FOR N% = 30 TO 1 STEP -1
 40   PRINT TAB(22,10) "  " TAB(22,10);N%;
 50   PROCwait_1_second
 60 NEXT
 70 PRINT TAB(0,10) "BLAST OFF";STRING$(14," ")
 80 END
 90
100 DEF PROCwait_1_second
110 TIME = 0
120 REPEAT
130 UNTIL TIME >= 100
140 ENDPROC

The important points about procedures are:

  • The procedure definition must start with DEF PROC (or, more simply, DEFPROC) followed by the procedure name. There must be no spaces between PROC and the name.
  • The procedure definition must end with the keyword ENDPROC.
  • Procedures are called by the keyword PROC followed by the procedure name, again with no spaces.
  • Procedure names obey the same rules as variable names, except that they are allowed to start with a digit and may include the character @. Procedure names can also include or start with reserved words, e.g. PROCTO.
  • The main body of the program must be separated from the procedure definitions by an END statement. That is, you should only enter the body of a procedure by a PROC statement, not by 'falling' into it. DEF statements are treated as REMs if they are encountered in the usual execution of a program.

Procedures enable you to split up a large amount of code into smaller distinct sections which are easy to manage. The main body of a program can then consist almost entirely of procedure calls, so that it can remain short and easy to follow (since it should be obvious from the procedure names what each call is doing).

Parameters and local variables

Consider the following program:

 10 REM Draw boxes centred on the screen
 20 MODE 12
 30 FOR N% = 1 TO 10
 40   PRINT "What size do you want the next box to be ";
 50   INPUT size
 60   IF size<1024 PROCbox(size) ELSE PRINT "Too large"
 70 NEXT N%
 80 END
100 DEF PROCbox(edge)
110 RECTANGLE 640-edge/2, 512-edge/2, edge, edge
120 ENDPROC

The procedure PROCbox draws a box around the centre of the screen. The size of this box is determined by the value of the variable edge. This variable has the current value of size assigned to it each time the procedure is called from line 60. The values being passed to the procedure are known as actual parameters. The variable edge used within the procedure is known as a formal parameter.

A procedure can be defined with more than one parameter. However, it must always be called with the correct number of parameters. These parameters may be:

  • integers
  • floating point numbers
  • strings
  • arrays.

If a string variable is used as a formal parameter, it must have either a string expression or a string variable passed to it. Floating point and integer parameters may be passed to one another and interchanged freely, but remember that the fractional part of a floating point variable is lost if it is assigned to an integer variable. Array formal and actual parameters must be of exactly the same type. That is, if the formal parameter is an integer, then only integer arrays may be passed as actual parameters.

Local variables

The formal parameters of a procedure are local to that procedure. This means that assigning a value to any variable within the procedure does not affect any variable elsewhere in the program which has the same name. In the following program, the procedure PROCsquare has a parameter S% which is automatically local. It also contains a variable, J%, which is declared as being LOCAL.

 10 FOR I% = 1 TO 10
 20   PROCsquare(I%)
 30   PROCcube(I%)
 40 NEXT
 50 END
 60
100 DEF PROCsquare(S%)
110 LOCAL J%
120 J% = S% ^ 2
130 PRINT S% " squared equals "J%;
140 ENDPROC
150
200 DEF PROCcube(I%)
210 I% = I% ^ 3
220 PRINT " and cubed equals ";I%
230 ENDPROC

In the case of PROCcube, the actual parameter passed and the formal parameter referred to within it are both called I%. This means that there are two versions of the variable, one inside the procedure and another outside it.

Adding the line

35 PRINT I%

to the program above prints out the numbers 1 to 10, showing that the assignment to I% within PROCcube does not affect the value of I% in the main body of the program.

Declaring local variables

It is good practice to declare all variables used in a procedure as local, since this removes the risk that the procedure will alter variables used elsewhere in the program.

When you declare a local array, the LOCAL statement must be followed by a DIM statement to dimension the local array. For example, consider the following function which, when passed two vectors of the same size, returns their scalar product:

100 DEF FNscalar_product(A(),B())
110 REM ** Both arrays must have a dimension of 1 **
120 IF DIM(A()) <> 1 OR DIM(B()) <> 1 THEN
130 PRINT "Vectors required"
140 =0
150 ENDIF
160 REM ** Both arrays must be the same size **
170 IF DIM(A(),1) <> DIM(B(),1) THEN
180 PRINT "Vectors must be of same size"
190 =0
200 ENDIF
210 REM ** Create a temporary array of the same size **
220 LOCAL C()
230 DIM C(DIM(A(),1))
240 REM ** Multiply the corresponding elements and place in C() **
250 C() = A()*B()
260 REM ** Finally sum all the elements of C() **
270 =SUM(C())

This example uses a function instead of a procedure. Note that SUM is a built-in function.

Notice that although function definitions may be multi-line, the syntax is such that single line functions as found in older dialects of BASIC may be defined in a compatible manner. Thus you can say either:

1000 DEF FNdisc(a,b,c)
1010 REM find the discriminant of a, b and c
1020 =b*b-4*a*c

or, using the old-fashioned form:

1000 DEF FND(a,b,c)=b*b-4*a*c

(Another limitation of the non-BBC BASIC syntax was that often only single-letter function names were allowed.)

Value-result parameter passing

The simple parameter passing scheme described above is known as 'value' parameter passing because the value of the actual parameter is copied into the formal parameter, which is then used within the procedure. The result of any modification to the formal parameter is not communicated back to the actual parameter. Thus the formal parameter is entirely local.

BASIC provides a second method of parameter passing known as 'value-result'. This is just like the simple value mechanism in that the actual parameter's value is copied into the formal parameter for use inside the procedure. The difference is, however, that when the procedure returns, the final value of the formal parameter is copied back into the actual parameter. Thus, a result can be passed back. (This means that the actual parameter can only be a variable, not an expression.)

A statement specifying that you wish to pass a result back for a particular parameter should be preceded by the keyword RETURN. For example:

100 DEF PROCorderedswap(RETURN A,RETURN B)
110 IF A > B SWAP A,B
120 ENDPROC

SWAP is a built-in statement to swap the values of two variables or arrays.

Specifying RETURN before an array formal parameter does not make any difference to the way the parameter is passed.

Arrays passed by reference

Arrays are always passed by reference. That is, the array formal parameter acts as an 'alias' for the actual parameter within the procedure or function, and if you change the elements of the formal parameter, the actual parameter will also be altered. If you want to simulate value passing of array parameters, you should use a local array of the same dimensions as the actual parameter, for example:

1000 DEF PROCfred(a())
1010 LOCAL b()
1020 DIM b(DIM(a(),1), DIM(a(),2)) :REM assume a() is 2D
1030 b()=a() : REM now b() can be altered at will
1040 ...
LOCAL DATA and LOCAL errors

Because procedures and functions often set up their own error handlers and local data, it is possible to make these local so that nothing outside the procedure or function is affected. In fact, both these may be made 'local' outside of a procedure. For example, you can make an error handler local to a WHILE loop. However, the constructs are mentioned here for completeness. More information can be found about local error handlers in the Error Handling chapter.

To make the current DATA pointer local, and then restore it, a sequence of the following form is used:

1000 LOCAL DATA
1010 RESTORE +0
1020 DATA ...
1030 ...
1080 RESTORE DATA
...

LOCAL DATA stores the current data pointer (i.e. the place where the next READ will get its data from) away. It can then be changed by a RESTORE to enable some local data to be read, and finally restored to its original value using RESTORE DATA. Thus a procedure or function which uses its own local data can read it without affecting the data pointer being used by the calling program.

As mentioned above, LOCAL DATA and RESTORE DATA can be used anywhere that localised data is required, not just in functions and procedures. They can also be nested. However, if LOCAL DATA is used within a function or procedure definition, it must come after any LOCAL variables. BASIC will perform an automatic RESTORE DATA on return from a PROC or FN, so that statement isn't strictly required at the end of PROCs and FNs.

ON... PROC

ON ... PROC IS SIMILAR TO ON ... GOTO WHICH IS DESCRIBED IN THE Control chapter. It evaluates the expression given after the ON keyword. If the value N% is given, it then calls the procedure designated by N% on the list. For example:

 10 REPEAT
 20   INPUT "Enter a number ",num
 30   PRINT "Type 1 to double it"
 40   PRINT "Type 2 to square it"
 50   INPUT action
 60   ON action PROCdouble(num), PROCsquare(num)
 70 UNTIL FALSE
100 DEF PROCdouble(num)
110 PRINT "Your number doubled is ";num*2
120 ENDPROC
200 DEF PROCsquare(num)
210 PRINT "Your number squared is ";num*num
220 ENDPROC

Note, however, that in most circumstances, the CASE statement provides a more powerful and structured way of performing these actions.

Recursive procedures

A procedure may contain calls to other procedures and may even contain a call to itself. A procedure which does call itself from within its own definition is called a recursive procedure:

 10 PRINT "Please input a string :"
 20 INPUT A$
 30 PROCremove_spaces(A$)
 40 END
100 DEF PROCremove_spaces(A$)
110 LOCAL pos_space%
120 PRINT A$
130 pos_space%=INSTR(A$," "):REM =0 if no spaces
140 IF pos_space% THEN
150   A$=LEFT$(A$,pos_space%-1)+RIGHT$(A$,pos_space%+1)
160   PROCremove_spaces(A$)
170 ENDIF
180 ENDPROC

In the example above, PROCremove_spaces is passed a string as a parameter. If the string contains no spaces, the procedure ends. If a space is found within the string, the space is removed and the procedure is called again with the new string as an argument to remove any further spaces. For example, typing the string The quick brown fox causes the following to be displayed:

The quick brown fox
Thequick brown fox
Thequickbrown fox
Thequickbrownfox

Recursive procedures often provide a very clear solution to a problem. There are two reasons, however, which suggest that they may not be the best way to solve a problem:

  • Some operations are more naturally expressed as a loop, that is, using FOR ... NEXT_ REPEAT ... UNTIL_ OR WHILE ... ENDWHILE.
  • Recursive procedures often use more of the computer's memory than the corresponding loop.

As an example, the following two programs both print Good morning! backwards. The first one uses a WHILE ... ENDWHILE loop. The second uses a recursive technique to achieve the same result.

First example:

 10 PROCreverseprint("Good morning !")
100 DEF PROCreverseprint(A$)
110 FOR i% = LEN A% TO 1 STEP -1
120   PRINT MID$(A$,i%,1)
130 NEXT
140 ENDPROC

Second example:

 10 PROCreverseprint("Good morning !")
100 DEF PROCreverseprint(A$)
110 IF LEN(A$) > 0 THEN
120   PRINT RIGHT$(A$);
130   PROCreverseprint(LEFT$(A$))
140 ENDIF
160 ENDPROC

Functions

Functions are similar in many ways to procedures, but differ in that they return a result. BASIC provides many functions of its own, like the trigonometric functions SIN, COS, TAN and RND. If you give RND a parameter with an integer value greater than 1, it returns a random value between 1 and the number given inclusive. For example,

X = RND(10)

produces random numbers between 1 and 10.

You may define functions of your own using the keyword DEF followed by FN and the name of your function. The function definition ends when a statement beginning with an = sign is encountered. This assigns the expression on the right of the sign to the function result. This result may be assigned to a variable in the normal way.

Functions obey the same rules as procedures with regard to naming conventions, the use of parameters and local variables.

We have already seen an example function definition in FNscalar_product above. The following is another example of how a function may be defined and used:

  10 FOR N% = 1 TO 10
  20   PRINT "A sphere of radius ";N%;" has a volume "; FNvolume(N%)
  30 NEXT N%
  40 END
 100 DEF FNvolume(radius%)
 110 = 4/3*PI*radius%^3

Function and procedure libraries

Libraries provide a convenient way of adding frequently-used procedures and functions to a BASIC program.

The libraries are kept in memory (unless they are OVERLAYed), and if a reference is made to a procedure or function which is not defined in your program, a search of each library in turn is made until a definition is found. If the routine is found in a library, it is executed exactly as though it were part of the program.

The advantages of using libraries are:

  • They standardise certain routines between programs.
  • They reduce the time required to write and test a program. (The library routines only need to be written and tested once, not each time a new program is developed.)
  • They make programs shorter and more modular.
Loading a library into memory

There are three methods of loading a library into memory, INSTALL, LIBRARY and OVERLAY.

INSTALL and LIBRARY are followed by a string giving a filename. This file should contain a set of BASIC procedure and function definitions, perhaps with local DATA statements to be used by those procedures and functions.

INSTALL loads the library at the top of BASIC's memory. It then lowers the upper memory limit that BASIC programs can use. INSTALLed libraries are therefore 'permanent' in that they occupy memory (and may be called) until BASIC is re-started (e.g. by another *BASIC command). You can not selectively remove INSTALLed libraries. INSTALL must be used before the BASIC program is first run rather than from within a program - it is a command, and cannot be used as a program statement.

LIBRARY reserves a sufficient area of memory for the library just above the main BASIC program and loads the library. Any library loaded in this way remains only until the variables are cleared. This occurs, for example, when the CLEAR or NEW commands are given, when the program is edited in some way, or when a program is run. Thus LIBRARY-type libraries are much more transient than INSTALLed ones (as temporary as normal variables, in fact), so you would generally use LIBRARY from within a program.

For example:

 10 MODE 12
 20 REM Print out a story
 30 REM Load output library
 40 LIBRARY "Printout"
 50 REM Read and print the heading
 60 READ A$
 70 PROCcentre(A$)
 80 REM Print out each sentence in turn
 90 REPEAT
100   READ sentence$
110   REM if sentence$ = "0" then have reached the end
120   IF sentence$ = "0" END
130   REM otherwise print it out
140   PROCprettyprint(sentence$)
150 UNTIL FALSE
200 DATA A story
210 DATA This,program,is,using,two,procedures:
220 DATA 'centre',and,'prettyprint',from,a,library
230 DATA called,'Printout'.
240 DATA The,library,is,loaded,each,time,
245 DATA the,program,is,run.
250 DATA The,procedure,'centre',places,a,string,in,the
260 DATA centre,of,the,screen.
270 DATA The,procedure,'prettyprint',prints,out,
280 DATA a,word,at,the,current,text,cursor,
290 DATA position,unless,it,would,be,split,over,
300 DATA a,line,in,which,case,it,starts,the,word,
305 DATA on,the,next,line,down.
310 DATA 0

The library Printout could be as follows:

 10 REM >Printout - Text output library
 20 REM ************************************
 30 DEF PROCPrintouthelp
 40 REM Print out details of the library routines
 50 PRINT "PROCcentre(a$)"
 60 PRINT "Place a string in the centre"
 70 PRINT "of a 40 character line"
 80 PRINT "PROCprettyprint(a$)" 
 90 PRINT "Print out a word at the current"
100 PRINT "text cursor position, starting"
110 PRINT "a new 40 character line if required"
120 PRINT "to avoid splitting it over two lines"
130 ENDPROC140 REM ******************************* 
200 REM Place a string in the centre 
210 REM of a 40 character line 
220 DEF PROCcentre(a$) 
230 LOCAL start% 
240 start% = (40 - LEN(a$))/2 
250 PRINT TAB(start%);a$
260 ENDPROC
270 REM ******************************** 
300 REM Print out a word at the current 
310 REM text cursor position, starting 
320 REM a new 40 character line if required 
330 REM to avoid splitting it over two lines 
340 DEF PROCprettyprint(a$) 
350 LOCAL end% 
360 end% = POS + LEN(a$) 
370 IF end% < 40 PRINT a$;" "; : ENDPROC 
380 PRINT 'a$;" "; 
390 ENDPROC 
400 REM ********************************

In the above example the library Printout contains three procedures:

PROCPrintouthelp prints out details of the library structure
PROCcentre prints a string in the middle of a 40 character line
PROCprettyprint prints a word at the current position or, if necessary, on the next line to avoid splitting it

To make PROCprettyprint and PROCcentre more general-purpose a further refinement would be for them to take an additional parameter specifying the number of characters there are on the line, instead of a fixed length of 40.

Overlaying

OVERLAY enables you to give a list of filenames which contain libraries. When BASIC can't find a PROC or FN within the program or within any of the current libraries, it will start to look in the OVERLAY files. You give OVERLAY a string array as a parameter. For example:

10 DIM lib$(5)
20 lib$()="lib1","lib2","lib3","lib4"
30 OVERLAY lib$()
40 ...

When the OVERLAY statement is executed, BASIC reserves enough space for the largest of the files given in the string array. Then, when it can't find a PROC or FN definition anywhere else, it will go through the list, loading the libraries in order until the definition is found or the end of the array is met.

Once a definition has been found, that library stays in memory (and so the other definitions in it may be used) until the next time a definition can't be found anywhere. The search process starts again, so the current overlay library will be overwritten with the first one in the list. Once BASIC has found a definition, it will remember which file it was in (or more precisely, which element of the array held the filename), so that file will be loaded immediately the next time the definition is required and it is not in memory.

Because of the way one area of memory is used to hold each of the overlay files (and only one at any one time), you are not allowed to call a procedure whose definition is in an overlay library if one of the overlay definitions is currently active. Another way of putting this is that you can't nest overlay calls.

If you know that a given overlay file will never be needed again in the program, you can speed up the search through the overlay list by setting the no-longer-required elements of the array to the null string. You can also add new names to the end of the array, as long as none of the new library files is bigger than the largest one specified in the original OVERLAY statement.

You can execute OVERLAY more than once in a program. Each time it is called, the memory set aside for the previous set of files will be lost, and a new block based on the size of the new ones will be allocated.

Building your own libraries

There are certain rules which should be obeyed when writing library procedures and functions:

  • Line number references are not allowed.

    Libraries must not use GOTO, GOSUB, etc. Any reference to a line number is to be taken as referring to the current program, not to the line numbers with which the library is constructed. You can use RESTORE+ to access DATA statements in a library.

  • Only local variables referring to the current procedure or function should be used.

    It is advisable that library routines only use local variables, so that they are totally independent of any program which may call them.

  • Each library should have a heading.

    It is recommended that a library's first line contains the full name of the library and details of a procedure which prints out information on each of the routines in the library. For example:

    10000 REM>hyperlib - gives hyperbolic functions.
    Call PROChyperHelp for details

This last rule is useful because BASIC contains a command, LVAR, which lists the first line of all libraries which are currently loaded. As a result, it is important that the first line of each library contains all the essential information about itself.

This edition Copyright © 3QD Developments Ltd 2015
Last Edit: Tue,03 Nov 2015