Previous chapter Index Next chapter

Chapter 4

Building larger programs

Although a program is simply a number of instructions contained in program lines, in many ways it is more than the sum of its parts. One of the major factors affecting the success of a program is the way that you organise the instructions in it to form the program. The organisation of the program (its structure) is the topic of this chapter.

4.1 Compartmentalising a program

Very short programs are usually easy to understand and follow; as long as you know what the individual instructions do, you can figure out what the whole program does - line 10 gets a value, line 20 multiplies it by 3, line 30 prints the result and so on. As soon as a program gets beyond about ten lines long or starts to use complex instructions, this is no longer the case. A program which is difficult to understand is difficult to write and doubly difficult to change or correct!

The way to make a long or complex program easier to understand is to break it down into smaller chunks and label each chunk with what it does. To break a program into chunks, decide what separate stages the task consists of and write instructions to complete each stage in turn, but between each stage and the next, enter a program line consisting of just ' on its own. To indicate what a particular stage in the program does, use REM. This is a strange keyword - it simply tells BASIC to ignore the rest of the line! This allows you to type comments after the REM, usually to explain what is going on.

You will see both methods being used throughout the example programs in this introduction to BASIC. To give you a taste, contrast the following programs:

10  title$ = "Markup program"
20  markup = 0.5
30  PRINT title$:PRINT
40  INPUT "Product price";price
50  INPUT "Product name";name$
60  markup.price = price*(1+markup)
70  vat = 0.15
80  INPUT "How many";number
90  value = number*markup.price*(1+vat)
100 PRINT "Your stock of ";name$; " should cost ";value

10  REM ** set up variables **
20  '
30  vat = 0.15
40  markup = 0.50
50  title$ = "Markup program"
60  '
70  REM ** set up screen **
80  '
90  PRINT title$: PRINT 
100 '
110 REM ** get input **
120 ' 
130 INPUT "Product price";price
140 INPUT "Product name";name$
150 INPUT "How many";number
160 '
170 REM ** calculate value including vat and markup
180 '
190 markup.price = price*(1+markup)
200 value = number*markup.price*(1+vat)
210 '
220 REM ** display value **
230 '
240 PRINT "Your stock of ";name$; " should cost ";value
Although the second, neatly structured, program is somewhat longer, it is far easier to understand and check. For example, have you assigned the value to vat before using it? In the first version, you have to hunt for an answer; in the second, all constants are assigned to variables in one place, making it easy to check.

If you are going to write programs of any size or complexity, you should try to write them like the second version. Changing a program like the first will be a real nightmare!

4.2 Sequences and loops

The programs you have written so far have all been executed as a list of instructions, starting at the lowest line number and ending after executing the highest. There are however many ways to change the order in which program lines are executed.

The first of these is the GOTO command, which is followed by a line number: the target line. When this instruction is executed, BASIC continues execution from the target line specified rather than the next line in number sequence.

This may seem a little silly at first glance, but try the effect of adding the following line to the example program named "MARKUP". Load the program which you saved earlier by typing:

    LOAD "markup"

Now add the new line by typing:

    40 GOTO 10

The complete program is now:

10 INPUT "Type the item cost price, then press RETURN ", cost         
20 profit=20/100
30 PRINT "The price to charge is ";cost+cost*profit
40 GOTO 10

Now run the program. See how much easier it is to use the revised program: if you want to calculate the price to charge for a number of items, instead of having to run the program once for each item, it is ready for you to type the next cost price as soon as it has displayed the charge price for the previous item.

This revised version of the program using GOTO is all very well, but now the program never stops! The only way to get out of the program is to press [Extend Mode]+C, which immediately stops the BASIC program.

While this approach is quite acceptable in a simple program like this, it does not help you to write programs in which you will want to leave the repeated section and carry on executing another part of the program. A far better solution is described below.

Repeating the same sequence of instructions over and over again like this is called 'looping', with the repeated instructions called a 'loop'. Looping is used so frequently that BASIC provides commands to define and execute loops.

The first type of loop starts with the command FOR and ends with the command NEXT. The simplest form of this loop is as follows:

  FOR counter=start TO finish
    instructions
  NEXT

When it executes the FOR instruction, BASIC first checks whether start is less than or equal to finish. If it is not, BASIC skips the whole loop. If it is, BASIC sets the numeric variable counter to the numeric value start, then obeys the following instructions until it reaches the NEXT command. BASIC then adds 1 to the counter variable and checks whether its new value is greater than the numeric value of finish. If not, the instructions inside the loop are executed again, counter is increased by 1, then tested against finish again. Only when counter is greater than finish does BASIC finish the loop and continue with the next instruction after the NEXT command.

This type of loop is usually called a 'FOR...NEXT loop', for obvious reasons.

The FOR...NEXT loop is most useful when the number of times the loop should be executed is known at the start of the loop. For example, to print a column of asterisks down the screen:

10 INPUT "How many asterisks";asterisks
20 FOR a=1 TO asterisks
30 PRINT "*"
40 NEXT

This type of loop is also very convenient if you want to keep a count of the number of times the instructions in the loop have been executed. For example, to program a 'times table':

10 INPUT "What number times table do you want";times%
20 FOR number%=1 TO 12
30 PRINT number%*times%
40 NEXT

See how the counter variable is used not only to control the loop, but also to generate the output.

4.3 Making decisions

So far, we have seen how the computer can perform calculations, store and retrieve information, even repeat sequences of commands over and over. All very impessive, but not enough to program many tasks; remember the problem with leaving the loop created using GOTO, above?

The vital missing link is the ability to make decisions. Without it, the computer can only function as a powerful calculator; with it, really versatile programs can be written which adjust their behaviour to the information they receive, even (in extreme cases) appearing to be 'intelligent'.

4.3.1 Choosing alternative statements

Decision-making in BASIC is mainly provided by the commands IF and ON. IF will be described first.

IF: IF is followed by a 'logical expression' that BASIC can test, such as 'count is grater than limit'. In its simplest form described here, if BASIC finds that the expression is true, it obeys the instructions following the keyword THEN on the line. If the expression is false, (for instance if count is 4 and limit is 6), these instructions will be skipped over and execution will continue on the next program line.

This simple version of the IF instruction takes the following form:
IF 
logical-expression
 THEN 
instructions
\----------v---------/ \----v---/
expression tested executed if true

Note that the whole structure of the IF command, the logical expression, THEN and the instructions that depend on it must all be on the same program line. This is one of the instances when you will often need to include several instructions on the same line (by separating them with colons).

In the example program named 'MARKUP', we could decide that to leave the loop, the user will type a cost price of zero. The logical expression to test if cost is equal to zero is cost=0. To leave the loop we could use GOTO to carry on execution with a program line after the loop (line 50). Type the following additional lines:
15 IF 
cost=0
 THEN 
GOTO 50
\----v---/ \----v---/
expression executed if true
50 PRINT "And now for the rest of the program ..."

The modified program is now:

10 INPUT "Type the item cost price, then press RETURN ", cost         
15 IF cost=0 THEN GOTO 50
20 profit=20/100
30 PRINT "The price to charge is ";cost+cost*profit
40 GOTO 10
50 PRINT "And now for the rest of the program ..."

Try running the program to check that it works correctly. Try typing positive (or even negative!) cost prices and see that the looping continues. Type a cost price of 0; this should display the message produced by line 50 and end the program.

IF can also be followed by the keyword ELSE, which allows you to specify that a group of instructions will only be executed if the expression is not true:
IF 
logical-expression
 THEN 
instructions-1
 ELSE 
instructions-2
\----------v---------/ \----v---/ \----v---/
expression tested if true if false

For example, consider the following program fragment:

90...
100 balance = balance + transaction
110 IF balance<0 THEN PRINT "**Overdrawn**": PRINT -balance
     ELSE PRINT "In credit" : PRINT balance
120 INPUT "What next?(Q-quit,T-transfer,I-invoice)";answer$
130...	 

Line 110 uses IF...THEN...ELSE to print a different message depending on the state of your bank balance.
THEN PRINT "**Overdrawn**":PRINT -balance
/
IF balance<0
\
ELSE PRINT "In credit":PRINT balance

Note that, as for IF...THEN, the whole structure of IF...THEN...ELSE must be on a single program line (though this can flow onto several screen lines).

ON: The ON keyword is followed by a numeric expression, GOTO and a list of line numbers:

ON numeric-expression GOTO line1,line2,line3, etc.

The numeric expression is evaluated when the instruction is executed to yield a number n. If n is between 1 and the number of line numbers in the list, execution continues at the nth line number in the list. If n is less than 1 or greater than the number of line numbers in the list, execution continues with the next instruction.

ON provides a neat way to select one from a number of choices. It is equivalent to a sequence of IF instructions:

  IF numeric-expression = 1 THEN GOTO line1
  IF numeric-expression = 2 THEN GOTO line2
  IF numeric-expression = 3 THEN GOTO line3
etc.

(ON is also used with a number of other commands - described below.)

4.3.2 The tests

As described above, the logical-expression is usually a statement of the relationship between variables and/or constants. This will usually use one of the following 'relational operators':
a < btrue if a is less than b
a <= btrue if a is less than or equal to b
a = btrue if a is equal to b
a <> btrue if a is not equal to b
a >= btrue if a is greater than or equal to b
a > btrue if a is greater than b

Typical logical expressions are:

  count1 < count2
  name$ = ""
  key <> pointer%*2+1
  file.absent.flag

These logical expressions are evaluated by BASIC to produce either 0 (for false) or -1 (for true). You may also use a numeric constant or variable as a logical expression, which BASIC will consider as 'true' unless it has a value of 0.

Logical expressions can also be combined and modified using logical operators. The most useful are NOT, AND and OR.

NOT is used to invert the sense of a logical expression, so if a > 6 evaluates to 'true', NOT (a > 6) evaluates to 'false'.

OR is used to combine two logical expressions; the result is 'true' if either (or both) are 'true'. Thus the result of (1 = 2) OR (3 = 3) is 'true' because while the first expression is 'false', the second is 'true'.

AND is used to combine two logical expressions; the result is 'true' only if both are 'true'. Thus the result of (1 = 2) AND (3 = 3) is 'false' because while the second expression is 'true', the first is 'false'.

(The logical operators actually perform what are bitwise operations on the integer values of -1 and 0 BASIC uses to represent true and false; see Section 5.1.5.)

4.3.3 Controlling a loop

IF causes BASIC to execute a group of instructions once if a condition is true. Another command, WHILE, is provided to make BASIC execute a loop while a condition is true.

The WHILE loop is started by the command WHILE and ended by the command WEND. It takes the form:

  WHILE logical-expression
    instructions
  WEND

When it executes the WHILE instruction, BASIC evaluates the logical-expression and if it is true, executes the instructions in the loop up to the WEND command, then evaluates the logical-expression again as if entering the loop for the first time. BASIC only skips the loop and continues with the instruction after the WEND command when the logical-expression is found to be false.

The WHILE loop is ideally suited to situations where the number of times a loop should be executed is not known when it is started. As the WHILE logical expression is tested again each time the WEND is executed, all the programmer has to do is make sure that this expression evaluates to false when the looping should stop.

4.4 Stopping the program

Left to its own devices, a BASIC program only stops when the last program line has been executed. You can also stop it at any time by pressing [Extend Mode]+C - a sort of emergency stop!

BASIC provides two commands to let the programmer make the program stop itself under different circumstances: END and STOP

END stops the program just like it stops when it runs out of program lines. END is mainly used to stop the program when it has finished its intended tasks but there are more lines in the program.

STOP stops the program and displays the number of the program line containing the stop command. STOP is mainly used when testing a program that works incorrectly, to provide an easy way to check which parts of the program are being executed. (The command CONT can be used to make BASIC resume execution from where it stopped.)

4.5 Organising the program

4.5.1 Review of program structure

You have already met several different ways of controlling the order in which lines are executed. The first examples above executed each program line just once, in numerical order, rather like a straight road, running from start to finish:

A 'straight line' program:

Next came programs that looped: the program still ran from start to finish, but along the way parts were repeated, rather like a road with a roundabout and a driver who couldn't decide which exit to take:

A program with looping:

Then came programs that made decisions; there was a fork in the road and the program took the left or right path depending on information it had been given or calculated when run:

A program with decision-making:

These different structures suit different types of tasks: the 'straight line' for a simple 'one-off' task; the loop for fixed repetitive tasks; the 'fork' for tasks whose details depend on the particular information received when the program is run.

In designing a program, you will usually find that a combination of these elements is called for: you should choose the type of structure best suited to the particular stage in the task.

4.5.2 Subroutines

BASIC provides one other fundamental way of organising parts of a program, which is suited to tasks that require the same or a very similar sequence of steps in several parts of the program. This structure is provided by the commands GOSUB and RETURN, and is called a 'subroutine'.

Take the following program fragments:

80  FOR A=1 TO entries%
90  PRINT name$(A)
100 NEXT
110 PRINT "***Press any key to continue***"
120 WHILE INKEY$="":WEND
130 PRINT:REM Blank line
...
200 FOR A=1 TO queries
210 PRINT equation$(A),result(A)
220 NEXT
230 PRINT "***Press any key to continue***"
240 WHILE INKEY$="":WEND
250 PRINT:REM Blank line
...

You will see that lines 110 to 130 and 230 to 250 are identical, apare from their line numbers. This duplication seems a bit wasteful of typing effort and program space, even more if it occurs more than twice in the program. A better solution is to write the duplicated program lines just once, as a subroutine. A subroutine is simply a sequence of program lines that end with the command RETURN, that can be used ('called') from anywhere in the rest of the program by using the command GOSUB followed by the line number of the first line in the subroutine.

The subroutine should be kept away from the main part of the program to make sure it will only be entered when the appropriate GOSUB instruction is executed. The best place is usually at the end of the program, after an END, as in the example below.

Using subroutines, this program fragment becomes as follows:

80  FOR A=1 TO entries%
90  PRINT name$(A)
100 NEXT
110 GOSUB 1000
...
200 FOR A=1 TO queries
210 PRINT equation$(A),result(A)
220 NEXT
230 GOSUB 1000
...
990 END
999 REM * subroutine to pause until any key pressed *
1000 PRINT "***Press any key to continue***"
1010 WHILE INKEY$="":WEND
1020 PRINT:REM Blank line
1030 RETURN

GOSUB is rather like the GOTO command, except that when BASIC gets to the RETURN command at the end of the subroutine, it leaves the subroutine and continues execution with the next instruction, after the GOSUB instruction that called it originally. If this is not clear, enter then run each of these two programs in turn:

10 REM example demonstrating GOTO
20 PRINT "First this, ";
30 GOTO 60
40 PRINT "This instruction never executed"
50 END
60 PRINT "then this, because of the GOTO"

10 REM example demonstrating GOSUB
20 PRINT "First this, ";
30 GOSUB 60
40 PRINT "and finally this, after the subroutine has finished"
50 END
60 PRINT "then this, from the subroutine " 
70 RETURN

Subroutines can also be used when the progran lines are not exact duplicates, by intelligent use of variables and condition testing. For instance, if the messages required in the above example were different, the particular message required could be assigned to a string variable before calling the subroutine, with the subroutine printing that string variable rather than a fixed message.

Subroutines don't just help you write more compact programs. They also make it easier to correct or improve your programs, because you only need to change the subroutine, rather than the many occurrences of the duplicated code throughout the program.

GOSUB may also be used with the ON keyword, to select one from a list of subroutines, depending on the value of a numeric expression. The instruction takes the form:

ON numeric-expression GOSUB line1,line2,line3, etc.

The numeric expression is evaluated when the instruction is executed to yield a number n. If n is between 1 and the number of line numbers in the list, execution continues at the nth subroutine in the list. If n is less than 1 or greater than the number of line numbers in the list, the whole instruction is skipped.

4.5.3 Program design using modules

Subroutines can also be used to write programs that don't have a great deal of duplication, providing a good way of compartmentalising the program. Writing programs this way makes the program much easier to design, develop and understand, by allowing you to break down the task into a hierarchy of subtasks. For example, consider the following task:

Each level in the hierarchy can then be translated into a subroutine. The highest level is very simple:

10 GOSUB 1000:REM prepare
20 GOSUB 2000:REM process orders
30 GOSUB 3000:REM tidy up
40 END
50 REM ************* SUBROUTINES ********************

Each of these major subroutines can now be designed and coded. For example, the main processing subroutine could be:

2000 REM *** process orders ***
2010 INPUT "To prepare an order, type Y RETURN", answer$
2020 WHILE answer$="Y"
2030   GOSUB 2100:REM get order
2040   GOSUB 2500:REM print order
2050   INPUT "Another? type Y RETURN for yes"; answer$
2060 WEND
2070 RETURN

Another level down the hierarchy:

2100 REM ** get order **
2110 GOSUB 2200:REM get name
2120 GOSUB 2300:REM get amount
2130 RETURN

And another:

2200 REM * get name *
2210 INPUT "Type the client name followed by RETURN",name$
2220 IF name$="" THEN GOTO 2210
2230 RETURN
2300 REM * get amount *
2310 INPUT "Type the amount, followed by RETURN",amount$
2320 IF amount$="" THEN GOTO 2310
2330 amount=VAL(amount$)
2340 IF amount <=0 THEN PRINT "The amount must be positive":GOTO 2310
2350 RETURN

And so on...

By segmenting the program in this way, you can make sure the structure of the program reflects the structure of the task, which in turn makes it easier to find the part of the program that is of interest.

It also allows you to write the program a bit at a time. Starting from the top, you write only one level down at a time, checking that each level works correctly before proceeding to the greater detail of the next level. This makes it easier to spot and correct errors, because you will always know the level which they have developed on.

The flow in a program with a subroutine can be pictured as:

4.5.4 User-defined functions

Subroutines provide a convenient way of 'packaging' instructions. BASIC provides another rather similar facility for packaging expressions, called 'user-defined functions'. These are functions that can be used ('called') anywhere that a BASIC function could be, once they have been defined in the program using a DEF FN instruction.

DEF FN is used in an instruction consisting of four parts:

  DEF FNfunction-name(parameter-list)= function-expression

The user function call takes the form:

  FNfunction-name(list-of-values)

The function name is chosen by the programmer and can be any valid variable name. The parameter list (which must be in round brackets) consists of one or more variable names ('formal parameters'), separated by commas. When the function is used, these variables are assigned the values in the corresponding positions in the function call. Note that these variables are entirely separate from any variables of the same name used anywhere else in the program.

The function expression can be any valid expression, but will usually use the values passed to it in the formal parmaeters to calculate a result. This is passed back as a result of the user-defined function.

The 'list of values' is a number of values (constants, variables, or expressions), separated by commas. There must be the same number of these values as parameters in the corresponding function definition and they must be compatible types ie. both numeric or both string.

Typical functions and function calls (using the example screen control codes) are:

10 DEF FNvat(gross) = gross-gross/1.15
20 cost = 100
30 PRINT "The vat on a vat-inclusive item costing ";cost;"is ";FNvat(cost)
...
10 DEF FNmean(n1,n2)=(n1+n2)/2
20 highest% = 42:lowest%=3
30 average = FNmean(highest%,lowest%)
40 PRINT average
...
10 esc$ = CHR$(27)
20 REM for a VT52 computer
30 DEF FNscreen$(x,y,text$) = esc$ + "Y" + CHR$(y+32) + CHR$(x+32)+text$
40 INPUT "Message ";message$
50 INPUT "Column ";col%
60 INPUT "Row ";row%
70 PRINT FNscreen$(col%, row%, message$)
80 GOTO 40
...
10 log2=LOG(2)
20 DEF FNlog2(x) = LOG(x)/log2
30 INPUT "Positive number ";number
40 PRINT "Logarithm to base to is ";FNlog2(number)
50 GOTO 30

When it is executed, the user function definition does not do anything other than define the function. It must be executed before the user function can be used. It is usual to group function definitions all in one place, either at the start of the program or in an initialisation subroutine.

User-defined functions provide a convenient but very limited alternative to subroutines when the processing required can be encapsulated into a single expression.


Previous chapter Index Next chapter