Contents:
2.0 The basics of modular
programming
2.1 Subs
2.2 Functions
2.3 Where to use SUBs and
FUNCTIONs
3.0 Going into the details
of subs
3.1 Getting data into modules
3.2 Global Variables
3.3 Module Interface
3.4 Commands you can't
use in a sub
4.0 Going into the details
of Functions
4.1 Using functions for
calculations
1.0 Introduction
When writing large programs in QBasic,
it is vital for the debugging process that the flow of the program can
be understood easily. If GOTO and GOSUB is used a lot, the program acquires
a spaghettilike structure, making it extremely hard to track down and remove
bugs in the source. This is why modular programming is such a good idea.
This particular method of programming lets you divide your program into
smaller tasks that can be solved individually, creating an easy-to-read
source code, that also takes less time to debug.
2.0 The basics of
modular programming
First of all, a module is a chunk of code
within a program that performs a specific task, and typically has a name
that describes that task. A module that collects input from the user should
therefore be called "GetInput" or something along those lines, but all
valid variablenames can be used. Giving the module a descriptive name does
however make it easier for yourself and other people to understand the
source. There are two types of modules: SUBs and FUNCTIONs. These two terms
will be used throughout the tutorial, so here's a brief description of
them:
2.1 Subs
SUB is short for 'Subroutine'. A SUB may
contain most QBasic statements, although some statements may be used only
in the main code. SUBs are useful when you need several lines of code to
complete a single task, for example when printing help-screens for the
user, or creating windows. All variables used in a SUB are local, which
means they start at 0 or null every time the SUB is called, unless they
are dimensioned as global variables in the main code (more on this later).
You can also pass variables from the main code to the SUB, or define the
SUB as STATIC, which means the variables are not reset when control is
passed back to the main code. Don't worry if you don't understand the above
at this point, everything will be explained in detail later.
2.2 Functions
Functions are just that. They are used
in the same manner as 'built-in' QBasic functions such as RND (random number),
SQR (square root) or LEN (length of a string). In general, if you can type
Variable = FunctionName(arguments) and get away with it, it's a function.
This is where FUNCTIONs are different from SUBs, a sub is called like a
normal QBasic statement, and functions are either assigned a variable (a%
= FunctionName) or used to determine the outcome of a true/false expression
(IF YesNo$ = "N" THEN...)
2.3 Where to use SUBs
and FUNCTIONs
Subs and functions may appear alike, but
their uses are completely different. The following paragraph should give
you some idea of when to use subs and when to use functions. A sub is best
suited for a routine that requires little interaction with the main code,
meaning that the outcome of any calculation in the sub shouldn't bear any
significance to the main module. Functions on the other hand often provide
a new value to a variable in the main code, and are therefore best suited
for long or complicated calculations. In short, use subs for long routines
and functions for long calculations.
3.0 Going into the
details of SUBs
A new sub is created by typing 'SUB SubName'
whereafter you get a blank screen with the words 'SUB SubName' and 'END
SUB'. The statements in your module go between these two lines of code.
It is considered good programming practice to add a few comment lines to
the top of the sub describing what it does and what variables from the
main code are used and/or changed.
3.1 Getting data into
modules
As mentioned before, all variables used
in subs and functions are local to the module, which means they are reset
when command is passed back to the main code, unless the sub is defined
as STATIC. If you want to use variables/strings from your main code in
the module, you have two options: Global variables or the module interface.
3.2 Global Variables
If a variable or string is defined as
global, it can be used and changed in all levels of the program (in all
modules). This kind of variable is essential for large programs that use
many variables for controlling the programflow. A global variable is defined
in the main code with the following statement:
DIM SHARED Varible, Variable1..... AS TypeA, Variable2 AS TypeB
Where Type is the type of variable you are currently defining, e.g. Integer, String, Single-precision etc. (See the QBasic help for more info on variable types). When using global variables you are also forced to plan your program ahead before typing the actual code. This often makes the result of your efforts much better, as your code will be efficient and easily readable if you have planned it properly. Constants defined with the CONST ConstName = Value% statement are also global and may be used in all subs and functions without any bother.
3.3 Module Interface
An alternative to global variables is
using the module interface. This has advantages over global variables in
some cases, but naturally it is also inferior on some points. If you are
writing a sub that performs a task based on the data passed to it from
the main code, a box drawing routine for example, you would use the module
interface for data-interchange. This way you could get away with typing
something like 'DrawBox (1, 1, 20, 20)' instead of having to give four
global variables a new value and then calling the sub. The module interface
is created by adding a set of paranthesis () to the 'SUB SubName' statement:
SUB SubName (Variable%, String$)
It is now possible to pass two variables to the module (an integer and a string), by typing
SubName a%, s$
In the sub, the variables a% and s$ will be known as Variable% and String$ as stated in the SUB SubName statement. You can have any amount of variables of any kind passed to a module. This does mean however, that every time the module is called, the correct number of arguments must be passed to it. This is partly where this method is inferior to global variables. When calling a sub with arguments, you must make sure that you type the arguments in the correct order, according to the declaration of the sub, and that the arguments passed are of the right datatype (integer, string etc).
3.4 Commands you can't
use in a sub
There are certain commands and statements
that won't work within a module. This doesn't mean that there are some
tasks that can't be solved with modules, just that you need to add a little
extra code to your main program. The most commonly used statements that
aren't allowed in modules are as follows:
CONST
- For defining global constants
DIM SHARED
- For defining global variables
DECLARE SUB / FUNCTION - For initiating a module in
a program
DATA
- Data statements can only be put in the main code
You might very well encounter other statements and commands that are illegal in modules, just try moving them into the main code.
4.0 Going into the
details of Functions
A function is created in the same manner
as a sub, by typing the following statement: FUNCTION FunctionName (ArgumentList%)
Same as before, you now have a blank screen with the name of the function
and the END FUNCTION statement. There are two main uses for functions.
The first is calculating a new value based on the arguments passed to the
function in the arguments list, the second is reading or calculating a
new value based on input from the user or gathered from an input device.
4.1 Using functions
for calculations
This is really practical if you need a
fixed number of operations done on a variable from the main code. You could
have a function that converted a string input from the user to all uppercase,
no commas and no left/right spaces. This could be useful in a text-adventure
for example. You would do this by typing the formatting code into your
function module, using the name of the module as your stringname:
FUNCTION ConvString$(Text$)
ConvString$ = UCASE$(ConvString$)
' Convert to uppercase
ConvString$ = LTRIM$(RTRIM$(ConvString$))
' Remove left/right spaces
.....
END FUNCTION
In the main code you would put something like
INPUT Text$
Text$ = ConvString$(Text$)
Whereafter your string Text$ will be changed to whatever your sub does to it. It saves a lot of space in the main module, and makes it easier to read.
4.2 Using functions
for getting new values
A function may also be used to create
a new variable. This means that the function collects input from the keyboard
or another input device, and gives it to the variable in question. For
example, if we wanted to create a function that waited for a single keypress
from the user, printed it on the screen, and passed control back to the
main program, we would have something like this:
FUNCTION GetChar$
DO
' Start reading-loop
Temp$ = INKEY$
' Read the keyboard
LOOP UNTIL Temp$ <> ""
' Loop until key pressed
PRINT Temp$
' Print the character on screen
GetChar$ = Temp$
' Pass the value to the main program
END FUNCTION
And in the main code:
KeyPress$ = GetChar$
Again, this drastically shortens our code and makes it easier to get a single character from the user. (Strictly, the above example is obselete as it could be substituted with the INPUT$ function) It is vital that you include the line 'FunctionName = Value' at some point in the function, otherwise the main program will recieve the value 0 or null from the function. In the example we have put GetChar$ = Temp$ to pass the value of Temp$ to the main program.
5.0 Summary
We have (hopefully) learnt that modular
programming has many advantages over normal programming, the biggest being
the improved readability of the source code. In reality, all there is to
modular programming is deviding your program into tasks and figuring out
the best way to interchange data between the modules. Modules are written
in the same manner as normal QBasic programs, although some statements
are illegal in modules (you'll know when you try to use them).
6.0 Closing words
I hope this tutorial has given you some
ideas for structuring your programs in a functional way, modular programming
really is the most important aspect of structured programming. I encourage
you to use modules in your programs, as you will probably be able to use
some of them again in later projects. Eventually you will have built up
your own set of QBasic commands to be used over and over again, saving
you precious time and letting you concentrate on the important parts of
your program. Thank you for reading this tutorial.