One of the reasons we use SUB (sub-routines), GOSUBS, and FUNCTIONS, is because they allow us to reuse blocks of code, without retyping them. Less typing means fewer typos which may be frustrating to debug. Once you are satisfied with the results of a block of code, you may want to "copy and paste" to another program to avoid re-inventing the wheel. Most blocks are designed to "do something" with variables, numeric or string, within the main program. However, you may be surprised by the "scope" of a variable; that is, whether it is local to a block of code and not seen by the main program, or GLOBAL and may be used throughout the program. I suspect the reason behind limiting scope originated when software began being developed by teams of coders, with each programmer being assigned responsibility for a small module or purpose. It simply would not do to have variables created which might be in use elsewhere, unless absolutely required.
Whether you're an old hand at coding, a beginner, or an experienced coder making the transition from another version of Basic, you may initially experience some confusion understanding the differences between SUB, GOSUB, and FUNCTION statements in JB. Alyce Watson has some great explanations in LB Newsletter #114, http://babek.info/libertybasicfiles/lbnews/nl114/index.htm. Most of the features in ALL the LB Newsletters are equally applicable to JB and you should bookmark the site for valuable tips and explanations.
Some values are GLOBAL by default and may be used anywhere within a program. Examples of these are listed in the help files under the topic SUB. Variables which are not global, may be declared at the beginning of your program using the GLOBAL statement:
GLOBAL diameter, height, name$... etc.
Use caution when declaring variables to have a GLOBAL scope. These variables can be modified in sub-routines and functions, returned to the main program, and may produce unexpected (even confusing!) results elsewhere. Limiting scope is great for large businesses with a team of programmers in their IT department, but what about the little guy at home? I hope this little introduction may clear up some questions.
'SUB/GOSUB/FUNCTION/Error NOTICE
[doDemo]
TIMER 0 'Turn off timer if used to end SELECT CASE.
CLS 'Begins every demo with a clear screen.
'Branch to choice.
PRINT "Do you wish to calculate..."
PRINT TAB(5); "the area of a circle? A"
PRINT TAB(5); "the area of a square? B"
PRINT TAB(5); "the area of a triangle? C"
PRINT TAB(5); "nothing. I wish to quit. Q"
INPUT "Please enter your choice... "; resp$
resp$ = UPPER$(resp$) 'Convert to capital letter.
'IF resp$="Q" THEN [close]
SELECT CASE resp$
CASE "A"
[circle]
PRINT "Calculate the area of a circle." 'Using FUNCTION
INPUT "Enter the radius in inches (1/2 the diameter): "; resp$
IF resp$ = "" THEN GOSUB [knucklehead] : GOTO [circle]
rad=VAL(resp$)
IF rad <= 0 THEN GOSUB [knucklehead] : GOTO [circle]
PRINT "The area of the circle is "; sqcir(rad); " sq/inches."
'The function name is "sqcir", the value passed is "rad"
CASE "B"
[square]
PRINT "Calculate the area of a square." 'Using CALL a SUB
INPUT "Enter the length in inches of any side: "; resp$
IF resp$="" THEN GOSUB [knucklehead] : GOTO [square]
side=VAL(resp$)
IF side <= 0 THEN GOSUB [knucklehead] : GOTO [square]
CALL doSquare side 'Pass the value of "side" byref to doSquare
PRINT side
CASE "C"
[triangle]
PRINT "Calculate the area of a triangle." 'Using GOSUB
INPUT "Enter the base in inches: "; base$
INPUT "Enter the height in inches: "; height$
IF base$="" or height$="" THEN GOSUB [knucklehead] : GOTO [triangle]
base=VAL(base$)
height=VAL(height$)
'If the user is messing with our mind, give him the Knudkle Head Prize!
IF base <= 0 OR height <= 0 THEN GOSUB [knucklehead] : GOTO [triangle]
GOSUB [calTriangle] 'No need to pass variables to a GOSUB
PRINT "The area of the triangle is "; area; " sq/inches."
CASE "Q"
GOTO [quit] 'Exit the program after 'Goodbye' message.
CASE ELSE
PRINT "My apologies, sir. Your response did"+ CHR$(13)+ _
"not match any of my selections."
TIMER 2500, [doDemo]
WAIT 'Wait for the TIMER event.
END SELECT
INPUT "Do again (Y/N)? "; resp$
[quit]
IF LEFT$(resp$,1)="Y" OR LEFT$(resp$,1)="y" GOTO [doDemo] _
ELSE PRINT "Okay, have a nice day!"+ CHR$(13) + _
"Come back to keep me company again."
TIMER 2000, [close]
WAIT 'Wait for the TIMER event.
[close]
CLS 'End with a clear screen.
END
'Subs, Subroutines, Functions, and error notice below here.
[calTriangle] 'Calculate the area of a triangle using a sub-routine.
area=(base*height)/2
RETURN
FUNCTION sqcir(alias) 'Return a value using FUNCTION
sqcir=(3.14159*alias^2) 'Do the calculation
END FUNCTION
SUB doSquare byref alias 'byref' returns the value of "alias" to the program.
alias=(alias^2)
END SUB
[knucklehead]
NOTICE "Knucklehead Response"+CHR$(13)+ _
"You did not enter a valid "+CHR$(13)+ _
"measurement. Try again!"
RETURN
Before variables from the main program can be used in a FUNCTION or SUB, they must be passed to that portion of code. By default, JB passes variables by value, which allows the routine to 'do something' while the variable in the main program remains unchanged. If, however, we intend to change the value in the main program, we pass it 'byref' and a new value will be assigned to the variable in the main program when execution returns, as in the SUB doSquare, above.
The first routine calculates the area of a circle when the user enters the radius into the program. We get the radius as a string variable, then convert it to a number using the VAL() function. Why do we do this? Have you ever watched America's Funniest Videos? People do strange things, either by accident or intent. Accepting a string value allows us several methods to check for errors, regardless of input. Here we check for a valid response and to see if any input at all was entered. If there was none, the program branches to a GOSUB and the user is awarded the Knucklehead Prize. Execution then returns to the main program and the user is allowed to try again. We also tested for negative values, just to see if the user was jerking our chain.
If the user entered an acceptable value it is passed to a user designed function, mathematics is performed, and the result is returned to the main program. JB provides numerous built-in functions, but if you need to create one yourself you can do so. In this demo, the function to calculate the area of a circle is named "sqcir" and the value passed is "rad" because this is really a "rad" concept. I like to put all my subs, GOSUBS and FUNCTIONS following the END statement in a program, so skip down to the FUNCTION statement for the example.
Notice when we left the [circle] block of code, we passed a variable named "rad", but in the function statement it is named "alias." This is permissible and even useful in the event we have a FUNCTION call from elsewhere in the program and the variable has a different name. Perhaps the user is somewhere in the Constellation Orion and the name is "glipptrisk"? The FUNCTION will work anyway using the value entered as resp$.
Functions must end with the END FUNCTION command. After the FUNCTION has done it's work, control returns to the next line in the demo and the result is printed using PRINT sqcir(rad). Text can be appended to the result to make it more meaningful when displayed. FUNCTIONS are useful in the event we must do something repeatedly, using a method or procedure which could not be anticipated or included by the creators of the programming language.
Then next block of code calculates the area of a square using a SUB routine. The statement to do something with a sub-routine is CALL label, in this case, CALL doSquare. The resulting answer is passed "byref" and can be 'seen,' by the main program, as well as changed by the sub-routine. If we do not want the value changed in the main program, we allow it to be passed "by value," which is the default unless JB is told otherwise. A sub-routine must have an END SUB command.
The last method in the demo is a GOSUB. All variables in the main program can be used by the GOSUB, vice-versa, any variables changed in the GOSUB will affect the values used in the main program. Because of this GLOBAL scope, it is not necessary to pass variables to a GOSUB.
The block to calculate the area of a triangle gets two inputs from the user, checks for valid input, then branches to GOSUB [calTriangle] where the area is calculated. Execution returns to the line following the GOSUB command and the result is printed.
In this demonstration code, a GOSUB was used for the error notice, because a SUB or FUNCTION cannot branch elsewhere in the program to print the selected error response. After the user has been notified of his faux-pas, we want to give him another chance to enter a response the program can use.
We attempted to anticipate common errors by the user and avoid needless program crashes. No one can anticipate every possible error or mistake, which is one reason "America's Funniest Videos" is such a popular TV show. Multiple errors could be compared using a SELECT CASE block of code and branch to different error responses, perhaps [knucklehead2] or [heyUdummy]. You're the coder and you can write the code as you prefer. Error messages should be informative so the user will know what he/she did wrong and can try again, but not too rude. Perhaps the mistake was in the way the coder evaluated the response which triggered the error message? I know it's hard to believe, but even good coders make mistakes.