It is never necessary to use line numbers. Instead of GOSUB you should use named user-defined procedures or functions. In the case of RESTORE you can use the relative option of the statement. You can avoid GOTO by making use of structures like REPEAT ... UNTIL and WHILE ... ENDWHILE and using the techniques described under Program Flow Control.
The RENUMBER command can be used to add line numbers automatically. Permissible line numbers are in the range 1 to 65535.
For example, the following will work.
10 FOR i=1 TO 5 PRINT i : NEXT
variables functions () ! ? & unary+- NOT ^ * / MOD DIV + - = <> <= >= > < << >> >>> AND EOR OR
IF A=2 AND B=3 THEN IF ((A=2)AND(B=3))THEN IF A=1 OR C=2 AND B=3 THEN IF((A=1)OR((C=2)AND(B=3)))THEN IF NOT(A=1 AND B=2) THEN IF(NOT((A=1)AND(B=2)))THEN N=A+B/C-D N=A+(B/C)-D N=A/B+C/D N=(A/B)+(C/D)
The following types of variable are allowed:
A real numeric
A% integer numeric
A& byte numeric
A$ string
PRINT NOT 1.5 -2 |
The argument, 1.5, is truncated to 1 and the logical inversion of this gives -2 |
PRINT NOT -1.5 0 |
The argument is truncated to -1 and the logical inversion of this gives 0 |
Two numeric functions, TRUE and FALSE, are provided. TRUE returns the value -1 and FALSE the value 0. These values allow the logical operators (NOT, AND, EOR and OR) to work properly. However, anything which is non-zero is considered to be TRUE. This can give rise to confusion, since +1 is considered to be TRUE and NOT(+1) is -2, which is also considered to be TRUE.
A limited number of arithmetic and logical operations are available which operate on entire arrays rather than on single values. These are addition (+), subtraction (-), multiplication (*), division (/), logical OR, logical AND, logical exclusive-or (EOR), integer quotient (DIV), integer remainder (MOD) and the dot product (.). For example:
Array expressions are evaluated strictly left-to-right, so higher priority operators must come first and brackets cannot be used to override the order of execution:C%() = A%() * B%() + C%() B() = A() - PI A() = B() . C() A() = B() * 2 / C() + 3 - A()
All arrays must be DIMensioned before use, and (with the exception of the dot product) the number of elements in all the arrays within an expression must be the same; if not, the Type mismatch error will result. In the case of the dot product the rules are as follows:C%() = C%() + B%() * A%() : REM not allowed C%() = A%() * (B%() + C%()) : REM not allowed
SUM may also be used with string arrays, it concatenates all the elements to form a single string (with a maximum length of 255 characters); the SUMLEN function sums the lengths of all the elements:mean = SUM(A()) / (DIM(A(),1) + 1)
The MOD function returns the modulus (square-root of the sum of the squares of all the elements) of a numeric array, so an array can be normalised as follows:whole$ = SUM(A$()) wholelen% = SUMLEN(A$())
MOD may also be used to calculate the RMS (Root Mean Square) value of a numeric array:A() = A() / MOD(A())
Don't confuse this with the MOD operator, which returns the remainder after an integer division.rms = MOD(array()) / SQR(DIM(array(),1) + 1)
You can use the compound assignment operators with arrays (e.g. +=, −=,*= or /=) but there is an important restriction. If the array is an integer (or byte) array the expression on the right-hand-side of the operator is converted to an integer before the operation is carried out. So the statement:
will multiply all the elements of array A%() by 2, not by 2.5.A%() *= 2.5
You can initialise the individual elements of an array in exactly the same way as you would normal variables. However you can also initialise the contents of an entire array in one operation:
If there are fewer values supplied than the number of elements in the array, the remaining elements are unaffected. However in the special case of a single value being supplied, all the elements of the array are initialised to that value. So in the last example all the elements of C() will be initialised to PI. There must be enough free space on the stack to contain a copy of the array.A%() = 1, 2, 3, 4, 5 B$() = "Alpha", "Beta", "Gamma", "Delta" C() = PI
Every time BBC BASIC encounters a FOR, REPEAT, WHILE, GOSUB, FN or PROC statement it 'pushes' the return address onto a 'stack' and every time it encounters a NEXT, UNTIL, ENDWHILE or RETURN statement, or the end of a function or procedure, it 'pops' the latest return address off the stack and goes back there.
Apart from memory size, there is no limit to the level of nesting of FOR ... NEXT, REPEAT ... UNTIL, WHILE ... ENDWHILE and GOSUB ... RETURN operations. The untrappable error message 'No room' will be issued if all the stack space is used up.
400 REPEAT 410 INPUT ' "What number should I stop at", num 420 FOR i=1 TO 100 430 PRINT i; 440 IF i=num THEN 460 450 NEXT i 460 UNTIL num=-1
REPEAT INPUT ' "What number should I stop at", num i=0 REPEAT i=i+1 PRINT i; UNTIL i=100 OR i=num UNTIL num=-1
REPEAT INPUT ' "What number should I stop at", num FOR i=1 TO 100 PRINT i; IF i=num THEN i=500 ELSE REM More program here if necessary ENDIF NEXT UNTIL num=-1
REPEAT INPUT ' "What number should I stop at", num FOR i=1 TO 100 PRINT i; IF i=num THEN EXIT FOR REM More program here if necessary NEXT i UNTIL num=-1
If you needed to know whether the loop had been terminated prematurely you could return a different value:DEF PROCloop(num) LOCAL i FOR i=1 TO 100 PRINT i; IF i=num THEN ENDPROC REM More program here if necessary NEXT i ENDPROC
DEF FNloop(num) LOCAL i FOR i=1 TO 100 PRINT i; IF i=num THEN =TRUE REM More program here if necessary NEXT i =FALSE
(BBC BASIC version 5 or later only)DEF PROC_error_demo FOR i=0 TO 10 LOCAL data(i) NEXT ENDPROC
Note the use of an opening and closing bracket with nothing in between.DEF PROC_error_demo LOCAL data() DIM data(10) ENDPROC
There are four indirection operators:
Name | Symbol | Purpose | No. of Bytes Affected |
---|---|---|---|
Query | ? | Byte Indirection Operator | 1 |
Pling | ! | Word Indirection Operator | 4 |
Pipe | | | Floating Point Indirection Operator | 5 |
Dollar | $ | String Indirection Operator | 1 to 256 |
?&4FA2=&23 or memory=&4FA2 ?memory=&23
number=?&4FA2 or memory=&4FA2 number=?memory
PRINT ?&4FA2 or memory=&4FA2 PRINT ?memoryThus, '?' provides a direct replacement for PEEK and POKE.
?A=B is equivalent to POKE A,B
B=?A is equivalent to B=PEEK(A)
DIM count% 0 FOR ?count%=0 TO 20 - - - - - - NEXT
!M=&12345678would load
&78 into address Mand
&56 into address M+1
&34 into address M+2
&12 into address M+3.
PRINT ~!M (print !M in hex format)would give
12345678
|F% = PIwould load the floating-point representation of PI into addresses F% to F%+4,
$M="ABCDEF"would load the ASCII characters A to F into addresses M to M+5 and &0D into address M+6, and
PRINT $Mwould print
ABCDEF
Thus,memory=&4000 memory?5=&50 PRINT memory?5
A?I=B is equivalent to POKE A+I,BThe two examples below show how two operands can be used with the byte indirection operator (?) to examine the contents of memory. The first example displays the contents of 12 bytes of memory from location &4000. The second example displays the memory contents for a real numeric variable. (See the Annex entitled Format of Program and Variables in Memory.)
B=A?I is equivalent to B=PEEK(A+I)
Line 30 prints the memory address and the contents in hexadecimal format.10 memory=&4000 20 FOR offset=0 TO 12 30 PRINT ~memory+offset, ~memory?offset 40 NEXT
See the Annex entitled Format of Program and Variables In Memory for an explanation of this program.10 NUMBER=0 20 DIM A% -1 30 REPEAT 40 INPUT"NUMBER PLEASE "NUMBER 50 PRINT "& "; 60 FOR I%=2 TO 5 70 NUM$=STR$~(A%?-I%) 80 IF LEN(NUM$)=1 NUM$="0"+NUM$ 90 PRINT NUM$;" "; 100 NEXT 110 N%=A%?-1 120 NUM$=STR$~(N%) 130 IF LEN(NUM$)=1 NUM$="0"+NUM$ 140 PRINT " & "+NUM$'' 150 UNTIL NUMBER=0
If M is the address of the start of the structure then:
$M is the stringLinked lists and tree structures can easily be created and manipulated in memory using this facility.
M?11 is the 8 bit number
M!12 is the address of the related structure
This will work for all types of variable (integer, floating-point, string, array etc.) but in the case of a normal string variable the address returned is not that of the first character of the string but of the 4-byte string descriptor (see the CALL statement for details of string descriptors). Therefore the address of the string itself is !^string$ >>> 16.A% = 1234 PRINT !^A%
In the case of an array the address returned by ^array() is that of a pointer to the array parameter block, therefore the address of the parameter block is !^array() AND &FFFF. To obtain the address of the array data you should specify the name of the first element, e.g. ^array(0).
Knowing the memory address of a variable may seem to be of little value but can be useful in special circumstances. For example In assembly language code you might want to copy the address of a BBC BASIC (integer) variable into one of the processor's registers:
Another use is to alter the byte-order of a variable, for example from little-endian to big-endian. The following code segment reverses the byte-order of the value stored in A%:ld hl,^variable%
SWAP ?(^A%+0), ?(^A%+3) SWAP ?(^A%+1), ?(^A%+2)
? | A unary and dyadic operator giving 8 bit indirection. |
! | A unary and dyadic operator giving 32 bit indirection. |
" | A delimiting character in strings. Strings always have an even number of " in them. " may be introduced into a string by the escape convention "". |
# | Precedes reference to a file channel number (and is not optional). |
$ | As a suffix on a variable name, indicates a string variable. As a prefix indicates a 'fixed string' e.g. $A="WOMBAT" Store WOMBAT at address A followed by CR. |
% | As a suffix on a variable name, indicates an integer (signed 32-bit) variable. As a prefix (BBC BASIC version 5 or later only) indicates a binary constant e.g. %11101111. |
& | As a suffix (BBC BASIC version 5 or later only) on a variable name, indicates a byte (unsigned 8-bit) variable. As a prefix Indicates a hexadecimal constant e.g. &EF. |
' | A character which causes new lines in PRINT or INPUT. |
( ) | Objects in parentheses have highest priority. |
= | 'Becomes' for LET statement and FOR, 'result is' for FN, relation of equal to on integers, reals and strings. |
== | (BBC BASIC version 5 or later only) Relation of equal to (alternative to =). |
- | Unary negation and dyadic subtraction on integers and reals. |
* | Binary multiplication on integers and reals; statement indicating operating system command (*DIR, *OPT). |
: | Multi-statement line statement delimiter. |
; | Suppresses forthcoming action in PRINT. Comment delimiter in the assembler. Delimiter in VDU and INPUT. |
+ | Unary plus and dyadic addition on integers and reals; concatenation between strings. |
, | Delimiter in lists. |
. | Decimal point in real constants; abbreviation symbol on keyword entry; introduce label in assembler; (BBC BASIC version 5 or later only) array dot-product operator. |
< | Relation of less than on integers, reals and strings. |
<< | Left-shift operator (signed or unsigned, BBC BASIC version 5 or later only). |
> | Relation of greater than on integers, reals and strings. |
>> | Right-shift operator (signed, BBC BASIC version 5 or later only). |
>>> | Right-shift operator (unsigned, BBC BASIC version 5 or later only). |
/ | Binary division on integers and reals. |
\ | Alternative comment delimiter in the assembler. |
<= | Relation of less than or equal on integers, reals and strings. |
>= | Relation of greater than or equal on integers, reals and strings. |
<> | Relation of not equal on integers, reals and strings. |
[ ] | Delimiters for assembler statements. Statements between these delimiters may need to be assembled twice in order to resolve any forward references. The pseudo operation OPT (initially 3) controls errors and listing. |
^ | Binary operation of exponentation between integers and reals; (BBC BASIC version 5 or later only) the address of operator. |
~ | A character in the start of a print field indicating that the item is to be printed in hexadecimal. Also used with STR$ to cause conversion to a hexadecimal string. |
(BBC BASIC version 5 or later only): | |
| | A unary operator giving floating-point indirection. A delimiter in the VDU statement. |
+= | Assignment with addition (A += B is equivalent to A = A + B) |
−= | Assignment with subtraction (A −= B is equivalent to A = A − B) |
*= | Assignment with multiplication (A *= B is equivalent to A = A * B) |
/= | Assignment with division (A /= B is equivalent to A = A / B) |
AND= | Assignment with AND (A AND= B is equivalent to A = A AND B) |
DIV= | Assignment with DIV (A DIV= B is equivalent to A = A DIV B) |
EOR= | Assignment with EOR (A EOR= B is equivalent to A = A EOR B) |
MOD= | Assignment with MOD (A MOD= B is equivalent to A = A MOD B) |
OR= | Assignment with OR (A OR= B is equivalent to A = A OR B) |
In general variable names cannot start with a keyword, but there are some exceptions. For example the constant PI and the pseudo-variables LOMEM, HIMEM, PAGE, and TIME can form the first part of the name of a variable. Therefore PILE and TIMER are valid variable names although PI$ and TIME% are not.
37 out of the total of 138 keywords are allowed at the start of a variable name; they are shown in bold type in the table below. 23 keywords are only available in BBC BASIC version 5 or later; they are indicated with an asterisk.
ABS | ACS | ADVAL | AND | ASC | ASN |
ATN | BGET | BPUT | BY* | CALL | CASE* |
CHAIN | CHR$ | CIRCLE* | CLEAR | CLG | CLOSE |
CLS | COLOR | COLOUR | COS | COUNT | DATA |
DEF | DEG | DIM | DIV | DRAW | ELLIPSE* |
ELSE | END | ENDCASE* | ENDIF* | ENDPROC | ENDWHILE* |
ENVELOPE | EOF | EOR | ERL | ERR | ERROR |
EVAL | EXIT* | EXP | EXT | FALSE | FILL* |
FN | FOR | GCOL | GET | GET$ | GOSUB |
GOTO | HIMEM | IF | INKEY | INKEY$ | INPUT |
INSTALL* | INSTR( | INT | LEFT$( | LEN | LET |
LINE | LN | LOCAL | LOG | LOMEM | MID$( |
MOD | MODE | MOUSE* | MOVE | NEXT | NOT |
OF* | OFF | ON | OPENIN | OPENOUT | OPENUP |
OR | ORIGIN* | OSCLI | OTHERWISE* | PAGE | PI |
PLOT | POINT( | POS | PROC | PTR | |
PUT | QUIT* | RAD | READ | RECTANGLE* | REM |
REPEAT | REPORT | RESTORE | RETURN | RIGHT$( | RND |
RUN | SGN | SIN | SOUND | SPC | SQR |
STEP | STOP | STR$ | STRING$( | SUM* | SWAP* |
SYS* | TAB( | TAN | THEN | TIME | TINT* |
TO | TRACE | TRUE | UNTIL | USR | VAL |
VDU | VPOS | WAIT* | WHEN* | WHILE* | WIDTH |
The following immediate-mode commands are strictly speaking not keywords: | |||||
AUTO | DELETE | EDIT | LIST | LISTO | LOAD |
NEW | OLD | RENUMBER | SAVE |
It is inevitable that programs will sometimes contain bugs. You may be able to write small programs which are error-free and work first time, but the larger your programs get the greater the likelihood that they will have errors. These fall into three categories:
when what you meant was:PRONT "Hello world!"
When the interpreter encounters the word PRONT it won't know what to do, and will report a Mistake error.PRINT "Hello world!"
is syntactically correct, and generally won't result in an error, but if variable B is zero it will result in the Division by zero error. It could also result in the Number too big error if A is very large and B is very small.answer = A / B
Errors of logic and computational errors are best avoided at the design stage, before you even start writing code, but this ideal situation is rarely achieved in practice.
ON ERROR ....and
ON ERROR LOCAL ....
If an error was detected in a program after this line had been encountered, the message 'Oh No!' would be printed and the program would terminate. If, as in this example, the ON ERROR line contains the END statement or transfers control elsewhere (e.g. using GOTO) then the position of the line within the program is unimportant so long as it is encountered before the error occurs. If there is no transfer of control, execution following the error continues as usual on the succeeding line, so in this case the position of the ON ERROR line can matter.ON ERROR PRINT '"Oh No!":END
As explained in the Program Flow Control sub-section, every time BBC BASIC encounters a FOR, REPEAT, GOSUB, FN, PROC or WHILE statement it 'pushes' the return address on to a 'stack' and every time it encounters a NEXT, UNTIL, RETURN, ENDWHILE statement or the end of a function or procedure it 'pops' the latest return address of the stack and goes back there. The program stack is where BBC BASIC records where it is within the structure of your program.
When an error is detected by BBC BASIC, the stack is cleared. Thus, you cannot just take any necessary action depending on the error and return to where you were because BBC BASIC no longer knows where you were.
If an error occurs within a procedure or function, the value of any parameters and LOCAL variables will be the last value they were set to within the procedure or function which gave rise to the error (the values, if any, they had before entry to the procedure or function are lost).
The following example program will continue after the inevitable 'Division by zero' error:
This is, of course, a poor use of error trapping. You should test for n=0 rather than allow the error to occur. However, it does provide a simple demonstration of the action of ON ERROR LOCAL. Also, you should test ERR to ensure that the error was the one you expected rather than, for example, Escape (ERR is a function which returns the number of the last error; it is explained later in this sub-section).FOR n=-5 TO 5 ok% = TRUE ON ERROR LOCAL PRINT "Infinity" : ok% = FALSE IF ok% PRINT "The reciprocal of ";n;" is ";1/n NEXT n
After the loop terminates (when 'n' reaches 6) the previous error trapping state is restored. Alternatively, you can explicitly restore the previous state using RESTORE ERROR.
You can call a subroutine, function or procedure to 'process' the error, but it must return to the loop, subroutine, function or procedure where the error was trapped:
The position of the ON ERROR LOCAL within a procedure or function can be important. Consider the following two examples:x=OPENOUT "TEST" FOR i=-5 TO 5 ok% = TRUE ON ERROR LOCAL PROCerr : ok% = FALSE IF ok% PRINT#x,1/i NEXT CLOSE#x : x=OPENIN "TEST" REPEAT INPUT#x,i PRINT i UNTIL EOF#x CLOSE#x END : DEF PROCerr IF ERR <> 18 REPORT:END PRINT#x,3.4E38 ENDPROC
DEF PROC1(A) ON ERROR LOCAL REPORT : ENDPROC LOCAL B ....
In the case of the first example, if an error occurs within the procedure, the formal parameter A will be automatically restored to its original value, but the LOCAL variable B will not; it will retain whatever value it had when the error occurred. In the case of the second example, both the formal parameter A and the LOCAL variable B will be restored to the values they had before the procedure was called.DEF PROC1(A) LOCAL B ON ERROR LOCAL REPORT : ENDPROC ....
At first sight ON ERROR LOCAL seems a more attractive proposition since BBC BASIC remembers where it is within the program structure. The one disadvantage of ON ERROR LOCAL is that you will need to include an error handling section at every level of your program where you need to trap errors. Many of these sections of program could be identical.
You can mix the use of ON ERROR and ON ERROR LOCAL within your program. A single ON ERROR statement can act as a 'catch all' for unexpected errors, and one or more ON ERROR LOCAL statements can be used when your program is able to recover from predictable errors. ON ERROR LOCAL 'remembers' the current ON ERROR setting, and restores it when the loop, procedure or function containing the ON ERROR LOCAL command is finished, or when a RESTORE ERROR is executed.
The example below does not try to deal with errors, it just uses ERR and REPORT to tell the user about the error. Its only advantage over BBC BASIC's normal error handling is that it gives the error number; it would probably not be used in practice. As you can see from the second run, pressing <ESC> is treated as an error (number 17).
Example run:ON ERROR PROCerror REPEAT INPUT "Type a number " num PRINT num," ",SQR(num) PRINT UNTIL FALSE : DEF PROCerror PRINT PRINT "Error No ";ERR REPORT END
The example below has been further expanded to include error trapping. The only 'predictable' error is that the user will try a negative number. Any other error is unacceptable, so it is reported and the program aborted. Consequently, when <ESC> is used to abort the program, it is reported as an error. However, a further test for ERR=17 could be included so that the program would halt on ESCAPE without an error being reported.RUN Type a number 1 1 1 Type a number -2 -2 Error No 21 Negative root
RUN Type a number <Esc> Error No 17 Escape
This is a case where the placement of the ON ERROR statement is important. When PROCerror exits, execution continues after the ON ERROR statement, which in this case causes the program to restart from the beginning.ON ERROR PROCerror REPEAT INPUT "Type a number " num PRINT num," ",SQR(num) PRINT UNTIL FALSE : DEF PROCerror PRINT IF ERR=21 THEN PRINT "No negatives":ENDPROC REPORT END
Example run:
The above example is very simple and was chosen for clarity. In practice, it would be better to test for a negative number before using SQR rather than trap the 'Negative root' error. A more realistic example is the evaluation of a user-supplied HEX number, where trapping 'Bad hex or binary' would be much easier than testing the input string beforehand.RUN Type a number 5 5 2.23606798
Type a number 2 2 1.41421356
Type a number -1 -1 No negatives
Type a number 4 4 2
Type a number <Esc> Escape
The next example is similar to the previous one, but it uses the ON ERROR LOCAL command to trap the error.ON ERROR PROCerror REPEAT INPUT "Type a HEX number " Input$ num=EVAL("&"+Input$) PRINT Input$,num PRINT UNTIL FALSE : DEF PROCerror PRINT IF ERR=28 THEN PRINT "Not hex":ENDPROC REPORT END
Note that had ON ERROR (rather than ON ERROR LOCAL) been used in this case, an error would give rise to a Not in a REPEAT loop error at the UNTIL statement. This is because ON ERROR clears the program's stack.REPEAT ON ERROR LOCAL PROCerror INPUT "Type a HEX number " Input$ num=EVAL("&"+Input$) PRINT Input$,num PRINT UNTIL FALSE : DEF PROCerror PRINT IF ERR=28 THEN PRINT "Not hex":ENDPROC REPORT END
Arguably, the major advantage of procedures and functions is that they can be referred to by name. Consider the two similar program lines below.
The first statement gives no indication of what the subroutines at lines 500 and 800 actually do. The second, however, tells you what to expect from the two procedures. This enhanced readability stems from the choice of meaningful names for the two procedures.IF name$="ZZ" THEN GOSUB 500 ELSE GOSUB 800
IF name$="ZZ" THEN PROC_end ELSE PROC_print
A function often carries out a number of actions, but it always produces a single result. For instance, the 'built in' function INT returns the integer part of its argument.
age=INT(months/12)A procedure on the other hand, is specifically intended to carry out a number of actions, some of which may affect program variables, but it does not directly return a result.
Whilst BBC BASIC has a large number of pre-defined functions (INT and LEN for example) it is very useful to be able to define your own to do something special. Suppose you had written a function called FN_discount to calculate the discount price from the normal retail price. You could write something similar to the following example anywhere in your program where you wished this calculation to be carried out.
It may seem hardly worth while defining a function to do something this simple. However, functions and procedures are not confined to single line definitions and they are very useful for improving the structure and readability of your program.discount_price=FN_discount(retail_price)
Function and procedure names may end with a '$'. However, this is not compulsory for functions which return strings.PROCPRINTDETAILS FNDISCOUNT
PROC_print_details FN_discount
DEF FN_average(A,B) = (A+B)/2 DEF PROC_clear:PRINT SPC(40-POS);:ENDPROC
DEF PROC_clear_to_end LOCAL i%,x%,y% x%=POS:y%=VPOS REM If not already on the last line, print lines of REM spaces which will wrap around until the last line IF y% < 24 FOR i%=y% TO 23:PRINT SPC(80);:NEXT REM Print spaces to the end of the last line. PRINT SPC(80-x%); REM Return the cursor to its original position PRINT TAB(x%,y%); ENDPROC
In this case, to use the function we would need to pass two parameters.DEF FN_discnt(price,pcent)=price*(1-pcent/100)
orretail_price=26.55 discount_price=FN_discount(retail_price,25)
orprice=26.55 discount=25 price=FN_discount(price,discount)
price=FN_discount(26.55,25)
The formal parameters must be variables, but the actual parameters may be variables, constants or expressions. When the actual parameter is a variable, it need not be (and usually won't be) the same as the variable used as the formal parameter. Formal parameters are automatically made local to the procedure or function.FOR I% = 1 TO 10 PROC_printit(I%) : REM I% is the Actual parameter NEXT END DEF PROC_printit(num1) : REM num1 is the Formal parameter PRINT num1 ENDPROC
You can pass a mix of string and numeric parameters to the procedure or function, and a function can return either a string or numeric value, irrespective of the type of parameters passed to it. However, you must make sure that the parameter types match up. The first example below is correct; the second would give rise to an 'Incorrect arguments' error message and the third would cause a 'Type mismatch' error to be reported.
PROC_printit(1,"FRED",2) END : DEF PROC_printit(num1,name$,num2) PRINT num1,name$,num2 ENDPROC
PROC_printit(1,"FRED",2,4) END : DEF PROC_printit(num1,name$,num2) PRINT num1,name$,num2 ENDPROC
PROC_printit(1,"FRED","JIM") END : DEF PROC_printit(num1,name$,num2) PRINT num1,name$,num2 ENDPROC
Declaring variables as LOCAL initialises them to zero (in the case of numeric variables) or null/empty (in the case of string variables). Declaring variables as PRIVATE causes them to retain their values from one call of the function/procedure to the next. If an array or structure is made LOCAL or PRIVATE it must be re-DIMensioned before use.
Variables which are not formal parameters nor declared as LOCAL are known to the whole program, including all the procedures and functions. Such variables are called global.
Since 'n' is the input variable to the function FN_fact_num, it is local to each and every use of the function. The function keeps calling itself until it returns the answer 1. It then works its way back through all the calls until it has completed the final multiplication, when it returns the answer. The limit of 35 on the input number prevents the answer being too big for the computer to handle.REPEAT INPUT "Enter an INTEGER less than 35 "num UNTIL INT(num)=num AND num<35 fact=FN_fact_num(num) PRINT num,fact END : DEF FN_fact_num(n) IF n=1 OR n=0 THEN =1 REM Return with 1 if n= 0 or 1 =n*FN_fact_num(n-1) REM Else go round again
BBC BASIC allows you to pass an entire array (rather than just a single element) to a function or procedure. Unlike single values or strings, which are passed to a function or procedure by value, an array is passed by reference. That is, a pointer to the array contents is passed rather than the contents themselves. The consequence of this is that if the array contents are modified within the function or procedure, they remain modified on exit from the function or procedure. In the following example the procedure PROC_multiply multiplies all the values in a numeric array by a specified amount:
Note the use of DIM as a function to return the number of elements in the array.DIM arr(20) FOR n%=0 TO 20 : arr(n%) = n% : NEXT PROC_multiply(arr(), 2) FOR n%=0 TO 20: PRINTarr(n%) : NEXT END : DEF PROC_multiply(B(), M) LOCAL n% FOR n% = 0 TO DIM(B(),1) B(n%) = B(n%) * M NEXT ENDPROC
The advantage of passing an array as a parameter, rather than accessing the 'global' array directly, is that the function or procedure doesn't need to know the name of the array. Ideally a FN/PROC shouldn't need to know the names of variables used in the main program from which it is called, and the main program shouldn't need to know the names of variables contained in a function or procedure it calls.
Although function and procedure parameters are usually inputs, it is possible to specify that they can also be outputs by using the keyword RETURN in the definition:
If a RETURNed parameter is modified within the function or procedure, it remains modified on exit. This is the case even if the actual parameter and the formal parameter have different names.DEF PROCcomplexsquare(RETURN r, RETURN i)
Suppose you want to write a function which returns a complex number. Since a complex number is represented by two numeric values (the real part and the imaginary part) a conventional user-defined function, which can return only a single value, is not suitable. Conventionally you would have to resort to using global variables or an array or structure to return the result, but by using RETURNed parameters this can be avoided.
The following example shows the use of a procedure to return the square of a complex number:
INPUT "Enter complex number (real, imaginary): " real, imag PROCcomplexsquare(real, imag) PRINT "The square of the number is ";real " + ";imag " i" END DEF PROCcomplexsquare(RETURN r, RETURN i) LOCAL x,y x = r : y = i r = x^2 - y^2 i = 2 * x * y ENDPROC
You can use a similar technique to return strings rather than numbers. The following example, rather pointlessly, takes two strings and mixes them up by exchanging alternate characters between them:
You can also use this facility to create a variable whose name is determined at run-time, something which is otherwise impossible to achieve:INPUT "Enter two strings, separated by a comma: " A$, B$ PROCmixupstrings(A$, B$) PRINT "The mixed-up strings are " A$ " and " B$ END DEF PROCmixupstrings(RETURN a$, RETURN b$) LOCAL c$, I% FOR I% = 1 TO LEN(a$) STEP 2 c$ = MID$(a$,I%,1) MID$(a$,I%,1) = MID$(b$,I%,1) MID$(b$,I%,1) = c$ NEXT I% ENDPROC
INPUT "Enter a variable name: " name$ INPUT "Enter a numeric value for the variable: " value$ dummy% = EVAL("FNassign("+name$+","+value$+")") PRINT "The variable "name$" has the value ";EVAL(name$) END DEF FNassign(RETURN n, v) : n = v : = 0
It is possible to declare an array within a function or procedure and return that new array to the calling code. To do that you must pass an undeclared array as the parameter and use the RETURN keyword in the function or procedure definition:
Note: You cannot use this technique to declare LOCAL arrays.PROCnewarray(alpha()) PROCnewarray(beta()) PROCnewarray(gamma()) DEF PROCnewarray(RETURN a()) DIM a(3, 4, 5) ... ENDPROC
|
CONTINUE
|