INDEX.HTM


RISC World

Programming for non programmers

Part 4 - Structured Programming

David Holden continues his series.

As programs become larger and more complex it is necessary to break them up into 'chunks'. If this is done sensibly and each part or sub-routine is made self contained this makes it a lot easier to write and debug the code.

Earlier versions of Basic made it almost impossible to write a properly structured program. The only means of altering program execution from simple line-by-line was the instruction GOTO which would jump directly to the line number which followed and GOSUB. Like GOTO this jumped to the line number which followed but when the program encountered the instruction RETURN it would return to the line after the one where the GOSUB occurred. This made it possible to control program execution but it was very difficult to work out what was actually happening as the program jumped from one line to another.

The GOTO, GOSUB and RETURN keywords still exist in BBC Basic, but now you know, forget about them. They belong to the dark ages of programming, and no sensible programmer would ever use them.

Procedures and Functions

These are a group of Basic statements, ideally but not necessarily self contained, which can be thought of as a modular piece of the program. They can be called by name from any other part of the program and, when the Procedure or Function is exited, program execution continues after the statement which called them.

If you take the code which performs a particular job and make it into a Procedure then, once you are sure it works properly, you can regard it as a 'black box' which simply does a job. It can be called from many different parts of your program and will always perform properly. If you write another program which needs code to do the same job you can simply paste your Procedure into it. Experienced programmers, whatever programming language they use, build up their own libraries of routines which they can incorporate into their programs.

The main difference between procedures and Functions is that Functions return a result or value. Procedures will not usually return values, although it is possible to make them do so.

Procedures and Functions can have data, more properly called parameters, passed to them. Because Functions are normally used to manipulate data and return a result they will almost always require parameters.

The easiest way to see how Procedures and Functions are used is to look at some examples. Let's go back to the VAT calculator from Part 2 which looked like this:

REM  Program to add or subtract VAT from a price
ON ERROR PRINT REPORT$ + " at line " + STR$(ERL) : END
REPEAT
 PRINT
 PRINT "Do you want to -"
 PRINT " 1. Add VAT to the net price"
 PRINT " 2. Calculate net price from VAT inclusive price"
 PRINT " 3. Exit the program"
 INPUT "Please choose " choice%
 CASE choice% OF
  WHEN 1 : INPUT "Enter price " price
           result = price * 1.175
           PRINT "Price with VAT is " result
  WHEN 2 : INPUT "Enter price " price
           result = price / 1.175
           PRINT "Price without VAT is " result
  WHEN 3 : END
  OTHERWISE : PRINT "You must enter 1, 2 or 3"
 ENDCASE
UNTIL choice% = 3
END

This is a fairly simple program and doesn't really need to be sub-divided, but it will serve to illustrate how this could be done. We could make the section which prints the menu into a procedure:

REM  Program to add or subtract VAT from a price
ON ERROR PRINT REPORT$ + " at line " + STR$(ERL) : END
REPEAT
 PROCmenu
 INPUT "Please choose " choice%
 CASE choice% OF
  WHEN 1 : INPUT "Enter price " price
           result = price * 1.175
           PRINT "Price with VAT is " result
  WHEN 2 : INPUT "Enter price " price
           result = price / 1.175
           PRINT "Price without VAT is " result
  WHEN 3 : END
  OTHERWISE : PRINT "You must enter 1, 2 or 3"
 ENDCASE
UNTIL choice% = 3
END
:
DEFPROCmenu
PRINT
PRINT "Do you want to -"
PRINT " 1. Add VAT to the net price"
PRINT " 2. Calculate net price from VAT inclusive price"
PRINT " 3. Exit the program"
ENDPROC

Note that PROCmenu is defined after the END statement. All Procedures and Functions must always be defined after the main body of the program.

The first line of the definition is DEFPROCmenu. DEFPROC is actually two keywords, DEF (for Define) and PROC (for Procedure). You could write this as DEF PROC with a space between them if you wish, although it is more usual to join them together. After this is the name that is assigned to the Procedure, in this case 'menu'.

There must not be a space between DEFPROC and the name given to the Procedure or Function. Names given to Procedures and Functions must conform to roughly the same rules as those given to variables, except that the name may begin with a number.

The procedure ends with the keyword ENDPROC. When this is reached the program will 'jump back' to the point after it was called in the body of the program. The Procedure is called by the keyword PROC followed by the name of the Procedure. Once again there must not be a space between PROC and the name. Now let's introduce some Functions to perform the calculations.

REM  Program to add or subtract VAT from a price
ON ERROR PRINT REPORT$ + " at line " + STR$(ERL) : END
REPEAT
 PROCmenu
 INPUT "Please choose " choice%
 CASE choice% OF
  WHEN 1 : INPUT "Enter price " price
           result = FNadd_vat(price)
           PRINT "Price with VAT is " result
  WHEN 2 : INPUT "Enter price " price
           result = FNsubtract_vat(price)
           PRINT "Price without VAT is " result
  WHEN 3 : END
  OTHERWISE : PRINT "You must enter 1, 2 or 3"
 ENDCASE
UNTIL choice% = 3
END
:
DEFPROCmenu
PRINT
PRINT "Do you want to -"
PRINT " 1. Add VAT to the net price"
PRINT " 2. Calculate net price from VAT inclusive price"
PRINT " 3. Exit the program"
ENDPROC
:
DEFFNadd_vat(amount)
= amount * 1.175
:
DEFFNsubtract_vat(amount)
= amount / 1.175

The variable 'price' is passed to the functions. When the function is called the value of 'price' is assigned to the variable 'amount'. It is important to understand that 'amount' is not the same variable as 'price'. It is a completely new variable, which is local to the Function, and has the same value as 'price'. Whatever is done to 'amount' within the Function, 'price' will not be changed.

All the variables we have used until now have been global. That is, they have ben created in the main body of the program and can be accessed and manipulated by any part of it. Local variables are a very powerful feature of Procedures and Functions. They are created when the Procedure is called, and destroyed when it exits. They cannot be accessed from the main body of the program because they only exist when the Procedure is executing. They can even have the same name as a global variable, but they will remain completely separate. For example, I could have written FNadd_vat as:

DEFFNadd_vat(price)
= price * 1.175

The variable 'price' inside the Function would have been a completely different variable from the global variable of the same name. Any changes made to 'price' inside the Function would be made to the local variable, and changes to 'price' made outside the Function would apply to the global variable.

The end of a Function definition is the '=' sign followed by the data which will be passed back to the program. This can, as in the example, be an expression. FNadd_vat could have been written as:

DEFFNadd_vat(amount)
amount = amount * 1.175
= amount

But the original was simpler. Because a Function can be used as a 'value' and hence be included in an expression we could simplify the program by PRINTing the value returned by the Function directly.

REM  Program to add or subtract VAT from a price
ON ERROR PRINT REPORT$ + " at line " + STR$(ERL) : END
REPEAT
 PROCmenu
 INPUT "Please choose " choice%
 CASE choice% OF
  WHEN 1 : INPUT "Enter price " price
           PRINT "Price with VAT is " FNadd_vat(price)
  WHEN 2 : INPUT "Enter price " price
           PRINT "Price without VAT is " FNsubtract_vat(price)
  WHEN 3 : END
  OTHERWISE : PRINT "You must enter 1, 2 or 3"
 ENDCASE
UNTIL choice% = 3
END
:
DEFPROCmenu
PRINT
PRINT "Do you want to -"
PRINT " 1. Add VAT to the net price"
PRINT " 2. Calculate net price from VAT inclusive price"
PRINT " 3. Exit the program"
ENDPROC
:
DEFFNadd_vat(amount)
= amount * 1.175
:
DEFFNsubtract_vat(amount)
= amount / 1.175

This time I have deleted the lines which assigned the value returned by the Functions to the variable 'result' and included the Functions directly in the PRINT statement.

This is a very complex subject, and so far we've only scratched the surface. As the series continues you will learn a lot more about these extremely useful and powerful objects.

The WHILE...ENDWHILE loop

I'll finish this month by describing the last of the looping structures supported by BBC Basic. The WHILE ... ENDWHILE structure is very similar to REPEAT ... UNTIL except that the comparison which decides whether the code inside the structure will be executed is at te start whereas with REPEAT ... UNTIL it's at the end. This means that whatever the circumstances a REPEAT ... UNTIL structure will always execute at least once, whereas if the conditions are not met a WHILE ... ENDWHILE structure will not execute at all. Lets look at two similar loops.

number% = 4
count % = 0
REPEAT
 number% = number% + 1
 PRINT number%
UNTIL number% >= count%

This loop is obviously only meant to continue until number% is greater than or equal to counts%. However, you will see that the code between REPEAT and UNTIL will always be executed, even though number% is greater than count% at the start of the loop, because the comparison isn't made until the end.

An equivalent WHILE ... ENDWHILE loop would be:


number% = 4
count% = 0
WHILE count% < number%
 number% = number% + 1
 PRINT number%
ENDWHILE

This time the comparison is made at the start, so if count% is greater than or equal to number% the code in the loop will never be executed.

In both cases if count% was greater than number% at the start of the loop then the program would go around the loop, incrementing number% each time, until number% was equal to count%, so the exit conditions would be the same in either case. Note that the logic of the comparison has to be reversed. In the REPEAT ... UNTIL loop we want to carry on until the condition is true. In the WHILE ... ENDWHILE loop we want to carry on while the condition is true.

As an exercise try changing the REPEAT ... UNTIL loop in the VAT calculator program into a WHILE ... ENDWHILE loop. Because we always want the program to execute at least once this wouldn't be the best choice, but it could be done. As a hint, the comparator '<>' means 'is less than or greater than' or 'is not equal to'. One solution is in the program ANSWER.

David Holden

 INDEX.HTM