Sometimes different parts of your program will have rather similar jobs to do, & you will find yourself typing the same lines in twice or more; however this is not necessary. You can type the lines in once, in the form known as a subroutine, & then use, or call, them anywhere else in the program without having to type them in again.
To do this, you use the statements GOSUB (GO to SUBroutine) & RETURN.
where n is the line number of the first line in the subroutine, is just like GOTO n except that the computer remembers the line number of the GOSUB statement so that it can come back again after doing the subroutine. It does this remembering by putting the line number (the return address) on top of a pie of them (the GOSUB stack).
takes the top line number off the GOSUB stack, & goes to the line after it.
As a first example,
10 PRINT "THIS IS THE MAIN PROGRAM",
20 GOSUB 1000
30 PRINT "AND AGAIN";
40 GOSUB 1000
50 PRINT "AND THAT IS ALL."
1000 REM SUBROUTINE STARTS HERE
1010 PRINT "THIS IS THE SUBROUTINE,"
The STOP statement in line 60 is very important because otherwise the program will run on into the subroutine & cause error 7 when the RETURN statement is reached.
For a less trivial example, suppose you want to write a computer program to handle pounds, shillings and pence. Those with long memories will remember that before 1971 a pound was divided into twenty shillings - so a shilling is 5p - & a shilling was subdivided into twelve old pence; d was the abbreviation for an old penny.) You will have three variables L, S & D (any maybe others - L1, S1, D1 & so on), and arithmetic is dead easy. First you do it separately on the pounds, shillings and pence - for instance, to add two sums of money, you add the pence, add the shillings and add the pounds; to double a sum of money you double the pence, double the shillings and double the pounds; and so on. When all that is done, adjust it to the correct form so that the pence are between 0 & 11, and the shillings between 0 & 19. This last stage is common to all the operations, so we can make it into a subroutine.
Laying aside the notion of subroutines for a moment, it is worth your while trying to write the program yourself. Give the arbitrary numbers L, S & D, how do you convert them into proper pounds, shillings & pence? Part of the problem is that you will start thinking of odder & odder cases.
What first springs to mind will probably be something like 1..25s..17d, which you want to convert to 2..6s..5d. Not so difficult. But suppose you have negative numbers? A dept of 1..25s..17d, or -1..-25s..-17d, might well turn out as -3..13s..7d, which is rather an odd way of expressing it (as though people only ever lend each other whole pounds). And what about fractions? If you divide 1..25s..17d by two, you get 5..12.5s..8.5d, & although this has the pence, 8.5, between 0 & 11; the shillings, 12.5, between 0 & 19, it is certainly not as good as 1..3s..2.5d. Try & work out your own answers to all this - & use them in a computer program - before you read any further.
Here is one solution.
1000 REM SUBROUTINE TO ADJUST L.S.D. TO THE NORMAL FORM FOR POUNDS, SHILLINGS AND PENCE
1010 LET D=240*L+12*S+D
1020 REM NOW EVERYTHING IS IN PENCE
1030 LET E=SGN D
1040 LET D=ABS D
1050 REM WE WORK WITH D POSITIVE, HOLDING ITS SIGN IN E
1060 LET S=INT (D/12)
1070 LET D=(D-12*S)*E
1080 LET L=INT (S/20)*E
1090 LET S=S*E-20*L
On its own, this is not much use because there is no program to set up L, S & d beforehand, nor to do anything with them afterwards. Type in the main program, & also another subroutine to print out L, S & D.
10 INPUT L
20 INPUT S
30 INPUT D
40 GOSUB 2000
45 REM PRINT THE VALUES
60 PRINT " = ";
70 GOSUB 1000
75 REM THE ADJUSTMENT
80 GOSUB 2000
85 REM PRINT THE VALUES
100 GOTO 10
2000 REM SUBROUTINE TO PRINT L,S AND D
2010 PRINT "£";L;"..";S;"S..";D;"D";
(Recall from chapter 9 that the empty PRINT statement in line 50 prints a black line.)
Clearly we have saved on program by using the printing subroutine at 2000, & this in itself is a very common use for subroutines: to shorten programs. However, the adjustment subroutine in fact makes the program longer - by a GOSUB & a RETURN; so program length is not the only consideration. Used with skill, subroutines can make programs easier to understand for the ones that matter, humans.
The main program is simplified by its using more powerful statements: each GOSUB represents some complicated BASIC, but you can forget that - only the net result matters. Because of this, it is much easier to grasp the main structure of the program.
The subroutines, on the other hand, are simplified for a very different reason, namely that they are shorter. They still use the same old plodding LET & PRINT statements, but they only have to do a part of the whole job & so are easier to write.
The skill lies in choosing the level - or levels - at which to write the subroutines. They must be big enough to have a significant impact on the main program, yet small enough to be significantly easier to write than a complete program without subroutines. These examples (not recommended) illustrate this.
10 GOSUB 1000
20 GOTO 10
1000 INPUT L
1010 INPUT S
1020 INPUT D
1030 PRINT " ";L;"..";S;"S..";D;"D";TAB 8;"=";
1040 LET D=240*L+12*S+D
10 GOSUB 1010
20 GOSUB 1020
30 GOSUB 1030
40 GOSUB 1040
50 GOSUB 1050
30 GOTO 10
1010 INPUT L
1020 INPUT S
1030 INPUT D
1040 PRINT " ";L;"..";S;"S..";D;"D";TAB 8; "=";
1050 LET D=240*L+12*S+D
The first, with its single powerful subroutine, & the second, with its many trivial ones, demonstrate quite opposite extremes, but with equal futility.
A subroutine can happily
call another, or even itself (a subroutine that calls itself is recursive),
so don't be afraid to having several layers.
1. The example program is virtually a universal LSD calculator. How would you use it.
(i) to convert pounds & new pence into pounds, shillings & pence?
(ii) to convert guineas into pounds & shillings? (1 guinea = 1..1s)
(iii) to find fractions of a pound? (e.g. a third of a pound, or a mark, is 6s..8d.
Put in a line to round
the pence off to the nearest farthing (1/4d).
2. Add two statements to the program:
4 LET ADJUST=1000
7 LET LSDPRINT=2000
GOSUB 1000 to GOSUB ADJUST
GOSUB 2000 to GOSUB LSDPRINT
This works exactly as you'd hope; in fact the line number in a GOSUB (or GOTO or RUN) statement can be any numerical expression. (Don't expect this to work on computers other than the ZX81, because it is not standard BASIC.)
This sort of stuff can
work wonders for the clarity of your programs.
3. Rewrite the main program in the example
to do something quite different, but still using the same subroutines.
4. ... GOSUB n
in consecutive lines can be replaced by
... GOTO n
5. A subroutine can have several entry points. For instance, because of the way our main program uses them, with GOSUB 1000 followed immediately by GOSUB 2000, we can replace our two subroutines by one big one that adjusts L, S & D & then prints them. It has two entry points: one at the beginning for the whole subroutine, & another further on for the printing part only.
Make the necessary rearrangements.
6. Run the program:
10 GOSUB 20
20 GOSUB 10
The return addresses are pushed on to the GOSUB stack in droves, but they never get taken off again & eventually there is no room for any more in the computer. The program then stops with error 4 (see appendix B).
You might have difficulty in clearing them out again without losing everything, but this will work.
(i) Delete the two GOSUB statements.
(ii) Insert two new lines
The return addresses will be stripped off until you get error 7.
(iv) Change your program so you don't get the same thing happening again.
How does this work?
Previous: Chapter 13 Next: Chapter 15