Back To Basics - Part 7

In part seven of his series on Basic programming, Mark Moxon explains procedures and functions, and why they are so useful

This month it's time for another really important concept in Basic: procedures and functions. These two constructs form the basis for clean, easy to read and efficient programs, so it's worth spending quite a lot of time getting used to the ideas involved. I'll be looking at procedures now and functions next month.

As always, the best way to explain a new concept is to present an example: have a look at Listing 1.

Listing 1

REM >Listing1
:
ON ERROR REPORT:PRINT " at line ";ERL/10:END
MODE 0
DIM array$(28)
row%=0
PRINT TAB(0,29);STRING$(75, "-")
PRINT TAB(0,0);"*"
REPEAT
  CASE GET$ OF
    WHEN "Z","z":PROCmove(1)
    WHEN "A","a":PROCmove(-1)
    WHEN "E","e":PROCedit
    WHEN "R","r":PROCright
    WHEN "C","c":PROCcentre
    WHEN "L","l":PROCprint_line(1)
  ENDCASE
UNTIL FALSE
END
:
DEF PROCmove(dir%)
  PRINT TAB(0,row%);" "
  row%=row%+dir%
  IF row%<0 THEN row%=28
  IF row%>28 THEN row%=0
  PRINT TAB(0,row%);"*"
ENDPROC
:
DEF PROCedit
  INPUT LINE TAB(0,30);array$(row%)
  PRINT TAB(0,30);STRING$(75," ")
  PROCprint_line(1)
ENDPROC
:
DEF PROCright
  indent%=75-LEN(array$(row%))
  PROCprint_line(indent%)
ENDPROC
:
DEF PROCcentre
  indent%=INT((75-LEN(array$(row%)))/2)
  PROCprint_line(indent%)
ENDPROC
:
DEF PROCprint_line(indent%)
  PRINT TAB(1,row%);STRING$(75," ")
  PRINT TAB(indent%,row%);array$(row%)
ENDPROC

Although it might seem short for what it does, this listing implements a very basic word processor. Run the program and the screen will clear. At the bottom of the screen is a dashed line: below this is the editing areas. Above the line is the actual document.

Use the keys A and Z to move the star up and down the left-hand side: this star is the cursor showing the current line. At any time, press E to enter edit mode. A question mark will appear at the bottom of the screen, and you can type in any text you want, up to a maximum of 75 characters. When you press RETURN, this text appears on screen.

If you want to right-justify some text, simply position the cursor on that line, and press R; if you want to centre a line, position the cursor and press C; finally, to left-justify a line, press L.

How it works

Pretty simple stuff, but not too bad for such a short listing, and you can see how it could be extended. So, how do procedures come into all this?

A procedure is simply a block of code with a name. The procedure is defined using the DEF PROC command, as in lines 21, 29, 35, 40 and 45; this defines a procedure as the block of code between the DEF PROC and the next ENDPROC, so PROCedit, defined in line 29, is the name given to the code between lines 29 and 33.

If you have a look at the listing, you will see that there are five procedures in the program: PROCmove, PROCedit, PROCright, PROCcentre and PROCprint_line. The way I have written the program, these blocks of code move the cursor, allow you to edit a line, right-justify a line, centre a line, and print a line respectively.

If you have a look at the start of the program, you can see that after all the variable definitions comes a REPEAT-UNTIL loop, which contains a CASE construct. This CASE statement checks to see which key has been pressed, using the GET$ command (which is explained below), and depending on the key, calls the correct procedure.

Calling procedures

When Basic sees a command like PROCedit, it simply executes the block of code defined by DEF PROCedit. This makes the program really easy to understand. Looking at lines 10 to 17 (the CASE construct), it's easy to see that the program is checking the keypress, and if it's A or Z, it moves the cursor; if it's E, it edits the line; if it's R, it right-justifies; if it's C, it centres; if it's L, it simply prints the line normally. Assuming our procedures are written correctly, the program does what we want.

Arguments

Apart from making your program easy to follow, which is obviously important, procedures allow you to create a block of code to do a certain job, and then simply call this by name anywhere in the program, so you don't have to copy out the same piece of code again and again. An example of this use of procedures is PROCmove in Listing 1.

When we want to move the cursor, we need to remove the old star, change the variable which contains the position of the cursor (row%), check that we haven't fallen off the top or bottom of the screen, and then print a star in the new position.

Now all of this is the same regardless of which way the cursor is moving, except for the changing of row%. So wouldn't it make sense to col- lect all this code together under one name, and call it from two different places in the program? Too right, it would.

The way that we get around the problem of knowing which way to move the cursor is to pass a value to the procedure when we call it with PROCmove. We do this by including the value inside brackets after the name of the procedure, so to move the cursor down we use PROCmove(1), and to move it up we use PROCmove(-1).

In the definition we use DEF PROCmove(dir%), which means that the variable dir% will be assigned either 1 or -1, depending on whether the procedure is being called in line 11 or 12. Inside the procedure, all we need to do is add dir% to row%, and row% will be increased or decreased as we require.

A similar method is used with PROCprint_line, which takes the argument indent%. This proceedure prints out a line to right justify and centre gives the number of spaces the line should be indented from the left-hand side of the screen, so all PROCright and PROCcentre need to do is work out this value from the length of the line, and call PROCprint_line.

To left justify, we simply call the procedure with a value of 1.

There are a few points which have not been covered which are used in Listing 1, and before we move on it would be wise to cover them, just so you understand the program fully.

The first new concept is the expression GET$. When this is evaluated (for example in the assignment A$=GET$, or in a CASE statement as in line 10), the computer waits until you have pressed a key. The expression is then evaluated to a string containing the character you pressed. So, if the computer comes across the line:

A$=GET$

and you press the U key, then A$ will have the value "U".

The next concept is that of multiple WHEN choices, as in lines 11 to 16. If a WHEN statement is followed by a list of matches, separated by commas, then if any one of these choices matches the expression in the CASE line, then that line is executed. So, in Listing One, it doesn't matter if you press capital Z or lower case z, the program will still call PROCmove to move the cursor down a line.

New concept

The final new concept is that of INPUT LINE, as used in line 30. Remember how, with the normal INPUT command, you could enter multiple values separated by commas? In other words, the line:

INPUT a,b,c

would accept the input '1,2,3', asigning a=1, b=2 and c=3. However, this creates a bit of a problem with strings. If you have a line

INPUT A$

and type in 'Hello, my name is Colin', then A$ will only be assigned the value "Hello", as everything after the comma will be stripped.

However, INPUT LINE (which behaves in exactly the same way as normal INPUT) doesn't suffer from this problem, and will take the whole string as input. Obviously with our word processor we want all the input, and so have to use this new form.

That wraps it up for this month: next time it's on to functions and more about procedures. Au revoir.


Source: Acorn User 139 - February 1994
Publication: Acorn User
Contributor: Mark Moxon