Think of a function as a little trading booth: You give something to the merchant and you get something else in exchange.
Examples:
The square root function --You supply a number, say X, and you get back the square root of X. You give 4, you get back 2; you give 9, get back 3; give 2.25, get back 1.5; and so on.
The absolute value function -- You supply any number and get back the same number with no sign (unsigned). You give any positive number (or 0) and get the same number back. Supply a negative number, say -X and get X. Give 1, get 1; give -10, get 10; give -1.5, get 1.5; etc.
These are examples of numerical functions -- put in a number and out comes a number. This is the only kind of function we will consider in this chapter. In Chapter 9 you will learn about string functions.
The value you feed into a function (its input value) is called the argument; the function value (its output value) is referred to in this way:
<function name>(<argument>)
Examples:
--We give the name SQR to the square root function. If X is positive (or 0), SQR(X) is just the number which, multiplied by itself, equals X. That is:
SQR(X) · SQR(X) = X
Trying to call SQR(X) for some negative value of X gives you an FC error message (ILLEGAL FUNCTION CALL).
--We give the name ABS to the absolute value function. The value you get when you "plug in" (as mathematics teachers say) X is called ABS(X). ABS(1) is 1 ; ABS(2.3) is 2.3; ABS(-17) is 17.
If you specify an exact value for the argument, the function gives you a definite value. But you can also call a function with a variable or other numerical expression in the argument; then the function does not have a specific value until the variable or expression is evaluated.
Examples:
PRINT ABS(X*Y)
IF (SQR(A/B)>ABS(G)) THEN 40
LET HA+ABS(OS)
IF ABS(ATN(X)>SQR(COS(Z)+1) THEN SIN(X)+-SIN(X)
Other numerical functions and calculations of derived mathematical functions (secant, inverse hyperbolic cotangent, etc. are given in Appendix C.
Certain functions (called intrinsic functions) are built into Standard BASIC. The Sorcerer recognizes their names where it sees them, without having to be told how the functions work. These names are all reserved words. (When the Sorcerer sees an intrinsic function it knows it is not dealing with an array, despite the parentheses, since no array name can contain a reserved word.)
You can also define your own functions with a special statement, the DEF statement. The DEF statement tells the Sorcerer that a certain name is the name of a function, rather than an array, and it also tells the Sorcerer how the function works.
The DEF statement has this format:
DEF <function name>(<variable>)=<expression>
The function name obeys these rules:
The expression can be any numerical expression that contains the variable. The variable is called a "dummy variable"; it need not be the same when later used. As with all numeric variables, we limit the part after FN to two characters to avoid confusion and to facilitate conversion to other forms of BASIC.
You may wish to define your own square function to use in a program:
10 DEF FNSQ(M)=M*M
20 PRINT
30 FOR X=1 TO 10
40 PRINT FNSQ(X);
50 NEXT X RUN 1 4 9 16 25 36 49 64 81 100
Notice that while FNSQ was defined with the variable M, it was later called up with the variable X.
You may use the derived functions of Appendix D with DEF statements.
Examples:
Secant: DEF FNSC(M)=1/COS(M)
Inverse Hyperbolic Cotangent: DEF FNAR(X)=LOG((X+1)/(X-1))/2
While it might seem more convenient to call your function FNARCCOTH to distinguish it from other functions, as far as the Sorcerer is concerned, FNARCCOT and FNARCSEC are the same function. There are enough two-letter combinations that you won't run out of names for variables in one program.
The INT function turns any number into an integer (whole number). For an integer, INT(X)= X. Otherwise, if X is positive you get INT(X) by throwing away the fractional part of X (truncating), and if X is negative you get INT(X) by adding -1 and then throwing away the fractional part.
Examples:
INT(1.5)= 1
INT(-3.5) =-4
INT(-1)= -1
INT(.00001)= 0
INT(1)=1
INT(8.00001)=8
INT(-8.00001)=-9
INT(-.00001)=-1
INT(0)=0
INT(478.006)= 478
INT(-478.006)=-479
INT(.999999)=0
Notice that the INT function does not round numbers. Use the function to round numbers to the nearest integer by adding .5 to the expression, as in this program.
10 PRINT TAB(10);"NUMBER";TAB(20);"NUMBER ROUNDED"
20 PRINT
30 FOR X=1 TO 10
40 READ NR
50 PRINT TAB(10); NR;TAB(20); INT(NR+.5)
60 NEXT X
70 DATA 3.4,3.5,395.803,.49,.499999,.5,-1,-6.4,-6.6,-6.5
RUN
NUMBER NUMBER ROUNDED
3.4 3
3.5 4
395.803 396
.49 0
.499999 0
.5 1
-1 -1
-6.4 -6
-6.6 -7
-6.5 -6
Look at the last entry. As you can see, this method of rounding does not work end with a negative number that ends in exactly .5. You will see a way around the problem in a moment.
Use the INT function to round a positive number to any desired number of decimal places (or to any multiple of ten). Shift the decimal point to the end of the number (by multiplying or dividing by the correct power of ten), add .5, use the INT function to truncate the resultant number, and then shift the decimal point back to where it was (by now dividing, if you multiplied previously, and multiplying, if you divided, by the same power of ten).
10 PRINT
20 INPUT "ENTER A NUMBER";A
30 PRINT
40 PRINT "HERE IS THE NUMBER ROUNDED:"
50 PRINT "TO HUNDREDTHS" INT(A*100+.5)/100
60 PRINT "TO TENTHS" INT(A*10+.5)/10
70 PRINT "TO THE NEAREST WHOLE NUMBER" INT(A+.5)
80 PRINT "TO THE NEAREST TEN" INT(A/10+.5)*10
90 PRINT "TO THE NEAREST HUNDRED" INT(A/100+.5)*100
RUN
ENTER A NUMBER?348.778
HERE IS THE NUMBER ROUNDED:
TO HUNDREDTHS 348.78
TO TENTHS 348.8
TO THE NEAREST WHOLE NUMBER 349
TO THE NEAREST TEN 350
TO THE NEAREST HUNDRED 300
You may isolate individual digits of a number by using INT. You want to isolate the third digit of a seven-digit number, say 7654321. Truncate the number after the third digit:
INT (7654321/10000)= 765
Truncate the same number after the second digit and multiply that value by 10 to have numbers of the same length:
1l\lT(7654321/100000)·10=760
Subtract the second from the first, and the result is that third digit you were looking for:
765-760 = 5
10 REM A PROGRAM TO FIND THE NTH DIGIT OF A 7-DIGIT NUMBER.
20 PRINT
30 INPUT "GIVE ME A 7-DIGIT INTEGER";
40 INPUT "WHICH DIGIT SHALL I ISOLATE";N
50 PRINT
60 PRINT "DIGIT #"N"IS"INT(A/10^(7-N))-INT(A/10^(8-N))*10
RUN
GIVE ME A 7-DIGIT NUMBER?7654321
WHICH DIGIT SHALL I ISOLATE?3
DIGIT # 3 IS 5
In line 60 you could set off the two strings from the variable N with semicolons, this way:
60 PRINT "DIGIT #";N;"IS"INT(A/10^(7-N))-INT(A/10^(8-N))*10
We leave the semicolons off to show you that the absence of a delimiter between string and numeric variable produces the same result as if a semicolon were present. That is, the two versions of line 60 are equivalent.
The SGN function returns 1 for positive expressions, -1 for negative expressions and 0 for expressions with value 0.
Examples:
SGN(923·759)=1
SGN(-3·0007)=-1
SGN(0)=0
Now, here is the method, promised earlier, to round completely accurately any expression. Round the absolute value of the expression and multiply by the sign of the expression.
10 REM A PROGRAM TO ROUND ANY NUMBER
20 PRINT
30 INPUT "INPUT ANY NUMBER";A
40 PRINT "HOW DO YOU WISH IT ROUNDED (ENTER .01 FOR"
50 PRINT "HUNDREDTHS, .1 FOR TENTHS, 1 FOR ONES, 10 FOR"
60 PRINT "TENS, ETC.)";
70 INPUT N
80 PRINT "YOUR NUMBER ROUNDED TO THE NEAREST"N"IS ";
90 PRINT INT(ABS(A)/N+.5)*N*SGN(A)
RUN
INPUT ANY NUMBER? 123.456
HOW DO YOU WISH IT ROUNDED (ENTER .01 FOR
HUNDREDTHS, .1 FOR TENTHS, 1 FOR ONES, 10 FOR
TENS, ETC.)? .01
YOUR NUMBER ROUNDED TO THE NEAREST .01 IS 123.46
RUN
INPUT ANY NUMBER? -6.5
HOW DO YOU WISH IT ROUNDED (ENTER .01 FOR
HUNDREDTHS, .1 FOR TENTHS, 1 FOR ONES, 10 FOR
TENS, ETC.)? 1
YOUR NUMBER ROUNDED TO THE NEAREST 1 IS -7
RND produces random numbers. Give it a positive argument (the number within the parentheses) and it generates a sequence of random numbers, each one between 0 and 1. It doesn't matter what positive number you use for the argument: each time you call the RND function, it produces a different random number between 0 and 1.
Example:
10 INPUT X
20 PRINT "RANDOM NUMBER="RND(X)
30 GOTO 10
RUN
? 1
RANDOM NUMBER= .245121
? 4
RANDOM NUMBER= .305003
? 986
RANDOM NUMBER= .515163
? 1
RANDOM NUMBER= 1.7514E-03
? .0008
RANDOM NUMBER= .0583136
For each input you get a different random number, apparently not dependent on the seed. (The argument for RND is often called the seed). Try running this program to see a set of random numbers:
10 FOR X=l TO 50
20 PRINT RND(X);
30 NEXT
Run the program several times to verify that it produces a different set each time. Now change line 20 to read
20 PRINT RND(1);
and run that several times to verify that it does not matter what number is used within the parentheses, so long as it is positive.
Each negative number, however, does a strange thing. Each produces one number between 0 and 1 which always comes out the same. RND(-50), for example, is always .308606, while RND(-3) is always .308605. Change line 20, above, to
20 PRINT RND(-X);
see the interesting results.
RND(0) always returns the most recent number called up by the RND function, the last time you used it, no matter when that was (so long as you didn't reset the Sorcerer or turn it off).
Now how do you use these? If you want to generate different random numbers in a program, use a positive seed, usually the same one each time (as in the SORCERER'S DICE program, below). If you want to generate a repeatable sequence (which you can use to test your program), first initialize the program by giving RND a negative seed. When the program appears to work, remove the statement that gives RND its negative seed.
That is, it is a deliberate part of Standard BASIC that each negative number used as seed produces the same series. For testing a program, you may wish to see the same sequences repeated until the bugs are out of the program. Then, later, to use the program for its designed purpose, you wish to produce truly random sequences, beyond the operator's control.
For example, this program gives a non-repeating set of random numbers. Each run of the program gives a new and different set.
10 FOR X=1 TO 50
20 PRINT RND(1);
30 NEXT X
Run the program several times to see that each set of random numbers is different. But add this line
5 Q=RND(-1)
and each time you run the program you get the same set of numbers.
You can use a combination of RND and INT to produce numbers in any range you wish.
Examples:
INT(RND(X)*10) generates a random whole number between 0 and 9.
INT(RND(X)*1010)/10+100 generates a random number, with one
decimal place, between 100.0 and 200.0
inclusive.
(In each case X is any positive number.)
You can use these functions for games simulation. To simulate a number of dice throws, for instance, use this program:
10 REM SORCERER'S DICE
20 PRINT
30 INPUT "HOW MANY THROWS OF THE DICE";Y
40 PRINT
50 PRINT "THROW #";TAB(20);"THROW";TAB(40);"TOTAL"
60 PRINT
70 FOR X=1 TO Y
80 LET B=INT(6*RND(1)+1)
90 LET C=INT(6*RND(1)+1)
100 PRINT X;TAB(20);B;"+";C;TAB(40);B+C
110 NEXT X
By the way, RND does not produce "real" random numbers: it produces something called pseudo-random numbers. These numbers are random enough for most purposes, however. If you choose enough random numbers between 0 and 1, they should theoretically tend to average .5. Run this program to see how "good" the Sorcerer's random number generator is. Let it go for a long time, say to 50,000, before drawing any conclusions, and try the program several times.
10 NR=0:TL=0:REM INITIALIZE COUNTER AND ACCUMULATOR
20 PRINT"TRIAL","RANDOM NUMBER","AVERAGE"
30 PRINT
40 NR=NR+1
50 RN=RND(1)
60 TL=TL+RN
70 AV=TL/NR
80 PRINT NR,RN,AV
90 GOTO 40
Writing to the screen greatly slows down the CPU (the Sorcerer's Central Processing Unit, a Z80 microprocessor). You can speed up this program by just having the results printed when they yet close to .5 and seeing how many trials that takes. Add these statements:
75 X=INT(TL*10000)
80 IF X=5000 OR X=4999 THEN PRINT NR,RN,AV
When you run the program, you may get tired of staring for a long time at a blank screen. Hit CTRL C , issue the direct mode command
PRINT NR,RN,AV
to see where the program is, and then type CONT. You may look at the results each hundred trials by adding one more statement:
85 IF INT(NR/188)-NR/100=0 THEN PRINT NR,RN,AV