Before I start, I'd like to explain that Gosub with subroutines is not in the same category as Goto. Using subroutines is GOOD programming practice and using Goto is BAD.
Functions however were not designed to be used INSTEAD OF subroutines, but to be used WITH subroutines. So, using functions for everything is NOT a good idea when you are learning to program. (IMHO it isn't good when you know what you are doing either, but that's a matter for debate and as an experienced coder, you can program how you want).
The point is that functions were designed to run in their own isolated part of memory, have the variables they require to work passed to them and most importantly, generate a single piece of data as a result which gets passed back to the main program it was called from.
Definition Of A Function (Wikipedia):
The mathematical concept of a function expresses the intuitive idea that one quantity (the argument of the function, also known as the input) completely determines another quantity (the value, or the output).
Note that this implies that all functions generate a single value for their output.
So, if the code you place in a function needs to change
lots of data items (Eg. you need to define lots of global variable for it to work), or it doesn't have any values to pass back to the main program, then it shouldn't really be a function - it should be a subroutine. Period!
For proof, take a look at ANY of the functions which are built into Dark Basic - they all have () on the end, like Point(), RGB(), Object Position X() etc.
Notice that they all take a number of parameters and return just a single value as an output?
So, I'm not saying don't use functions, I'm saying that you should use functions appropriately - as they were intended.
Dark Basic Functions
Whereas a subroutine, (often called a 'procedure'), starts with a label and ends with a RETURN and is called using GOSUB, functions are a somewhat different beast. Although similar to look at code-wise as subroutines, functions in Dark Basic have three main differences:
1. Local Variables
All variables in a function are local as opposed to global. In programming terms global means that they are visible to
all of your program, whereas local means that they are only visible inside a function they are used in. The visibility of variables is called the variable's 'scope'.
In Dark Basic Classic however, proper global variables don't really exist officially as they cannot be declared, (unless you are using an IDE which supports them). DBPro does have global variables.
A normal variable in your main program can be seen inside a procedure, but not inside a function. So, although I'll refer to them as global variables, DBC users should think of them as being 'semi-global'.
Another thing to remember about local variables is that they can co-exist at the same time as other variables with the same name.
This is very important to remember as not knowing this can lead to some very hard to find bugs in your programs.
For example, if you set the variable A to equal 10 in your main program with A=10, then call a function in which you say A=20, when you exit the function what will A equal?
If you said 20 then you got it wrong! The answer is 10 because inside the function, the variable A would be a newly created local variable called A - entirely seperate from the previously created variable A outside the function. When you exit from the function, you revert back to the the original variable A, which is of course is still equal to 10.
This feature can be very useful, but if you are used to programming in other languages you can be lured into a trap, so beware.
Variables in BASIC do not need to be declared at the start of your program like in Delphi or C - they exist from the moment you refer to them. For example, if you write a DB program with just one line which says PRINT MYVAR then DB will quite happily initialise the variable containing the value 0 and print 0 on the screen. You don't have to tell DB beforehand that you are going to use the variable MYVAR in your program and that it is going to be an integer variable.
In other languages, local variables are 'destructive', which means that when you exit a function, any local variables are destroyed.
The next time you visit the function, the variables are created again as entirely new entities. However, in Dark Basic local variables in functions are 'non-destructive' which means that when you return to a function, the variables still exist and contain whatever values they did when you were last there. This 'feature' can be very useful if you know it's there, but an annoying source of difficult to trace bugs if you don't!
2. Entry & Exit Parameters
Functions can be used to do a specific task without any external information and then exit without returning any information. If no entry parameters are required, empty parenthesis can be used when calling the function and in the function header - or they can be omitted entirely. DB will accept either format.
Alternatively, functions can do a task calculated on the information supplied and then return information. One example of a return value would be a success value. A 1 returned would denote that the function's task was completed successfully whereas a 0 could denote that something went wrong.
As described above, all variables in a function are local, so you need a method to pass information from your main program to the function and this is done by calling the function with the required variables in a 'parameter list' which is enclosed in parethesis '()'. The function itself must have the exact same parameter list to accept the same variables. So, you would have functions something like these:
No Entry Or Exit Parameters:
Function StartScreen
CLS RGB(100,0,100)
Ink RGB(255,255,255),0
Print "Screen Now Cleared And Ready For Use!"
EndFunction
Using Entry And Exit Parameters:
Function MyFunction(Var1,Var2,Var3,StringVar$)
SLen=Len(StringVar$)
RetVal=Var1*Var2*Var3+SLen
EndFunction RetVal
It's fairly obvious here that the function called MyFunction() is passed a parameter list of four variables - the first three being integer numbers and the fourth a string. These variable names are used inside the function as local variables to calculate the value of RetVal. RetVal is then returned back to the calling function. The actual calculation is of course nonsense, but demonstrates how things work.
So, how are functions called? Well, it depends on whether you need to pass parameters or not and whether your function returns anything. The first example above neither requires or returns anything so it is called with:
StartScreen()
The second function needs three integer variables and a string so they are passed in the parameter list. The actual variable names used in the call need NOT be the same as used in the parameter list of the function header as the variable's contents are placed into local variables when they get there. Think of it as passing the contents of each variable to the function - not the variables themselves.
The function also returns the contents of the variable RetVal, so we must take that into account when calling the function. This is done by using a variable of the same type in the call. As our example function MyFunction() returns an integer, we use the following call:
ValueBack=MyFunction(MinLen,MaxLen,Score,"Elephant")
After calling the function, the variable ValueBack will contain the returned contents of RetVal from the function. The value returned can be an integer number, a real number or a string, but the subtle point is that in Dark Basic, functions can only return a single value which can be a bit limiting.
The answer in many programming languages is the declaration of true global variables which can also be accessed within functions.
Sadly, you don't have the ability to do this in Dark Basic Classic, though as it happens, the work-around is on a similar theme - using arrays. Remember though, using arrays is only a work-around and there are limitations. You can't pass arrays to functions or return them.
It was discovered a long time ago that in DB Classic, arrays behave like proper global variables and an array declared
at the start of a program can be seen, used and modified inside a function and is still intact upon exiting the function. Arrays declared inside a function remain local though and not accessible outside it. So using arrays, a function can return multiple values - or the equivalent anyway.
3. #Include Files
Functions can be used in Include files - something widely misunderstood by the newcomer to programming with Dark Basic.
In a nutshell, the idea is that they prevent you from having to write certain sections of often used code over and over again each time you write a new program. A good example is the keyboard/mouse control code which you use for controlling the game character or ship in your games. If all the actions for moving and firing etc. are all done with functions, then they can all be saved in a single file called, let's say Control.dba for example.
The next time you write a program which uses the same control method, all you have to do is put the Control.dba file into the same folder as your new program and use the following line as the first line of your program:
#Include "Control.dba"
When the program is run, all the functions in the #Include file become available - without you having to re-code them again. A big time saver - especially if you have functions to cover all possible control input methods. You'd never have to write a piece of input code again!
You must remember however, that Include files can ONLY contain lists of function and no code can reside outside of each function header and it's associated EndFunction. To the best of my knowledge, the REM statement is the only exception allowed.
Why Use Functions?
Well speed-wise, there is little or no difference between using a procedure and a function, so the main reason for using them would be for the ability to use local variables or the future grouping together of them in a #include file. (In #Include files you can ONLY have functions - nothing else).
In fact in some cases, a procedure is better due to the lack of the local variable problem.
Some users will swear by using functions for everything, which I find a little pointless, but at the end of the day, go with whichever you are happiest with.
TDK_Man