Structured Programming
By David Tordrup

1.0 Introduction
There is more to good programming than learning the keywords and commands of a language like QBasic, there is also a more abstract aspect known as Structured Programming. If you want to write long and complicated code that does more than print "Hello World!" on the screen, the spaghettistructure of old fashioned BASIC just won't cut it anymore. People who have been mucking about with GWBASIC and BASICA will know what I mean, but the days of line numbering are over, say hello to Structured Programming!

QBasic is the prime version of BASIC and gives us quite a few advantages over previous releases. For example, the source editor now consists of a user- friendly text editor with many functions, a great modular level interface and extensive on-line help. So why not use all this great stuff to it's full extent? This tutorial will help you take advantage of the many features QBasic offers to programmers today, so read on...

1.1 the project
During this tutorial a simple address database program will be constructed step by step. With each step the necessary keywords and statements will be introduced and explained, hopefully giving you a better picture of just how everything works in reality. Thanks to Larry (nonnypa@gte.net) for suggesting This approach.

2.0 The backbone of structured programming
One of the major elements of structured programming is planning your program before you actually write it, e.g. you know in advance roughly what your program will do, which variables and datastructures it will use, and what the outcome will be. Starting to write a program without having at least considered how it will work will often result in disasterous code.

Let's take a brief look at what our database program will contain:

As you can see, an overview of the final program makes everything look very simple and easily done. That's because it is, when you have a general plan of your program worked out offhand, the rest is just a matter of applying the correct QBasic commands and statements. To make this task even easier we use pseudo code. This is basically a detailed version of the above overview that let's us plan the structure of each individual routine in the program before we get down and dirty with the QBasic commands. Some programmers use a certain structure and layout for their pseudo code, the official pseudo code, but this is not at all necessary as long as you know what is going on in your preview. Consider the following code:

'{ Initialize Datatype }
'{ Allocate memory for records }
'{ Open random access file for read/write }
'
'  Option 'Browse Records/Enter new record'
'
'  Procedure 'Browse Records'
'    { Get records from file }
'    { Display records }
'  end procedure
'
'  Procedure 'Enter new record'
'    { Get input for new record }
'    { Append record to file }
'  end procedure
'
'{ End program }

Make any sense? It should do, it's just a plain English version of the QBasic program still to be written. We will use the above code in the example source code (SPEX10.BAS) for the address database. Concluding this short chapter, pseudo code can and should be used to create the framework of your program before you indulge in the code writing process. It lets you maintain a constant overview of your program, helps you target your concentration on one task and gives you something to stick to when everything goes wrong (and it will). In short, use it!

3.0 Creating userdefined datatypes
What is a datatype? Datatypes are things like Integers (16-bit numbers), Long Integers (32-bit numbers), Single Precision digits etc, in general just types of data. If you are working with arrays (stacks) of conventional data, you will only be able to manipulate one array at a time, for example:

A%(2) = A%(1)
B%(2) = B%(1)
C%(2) = C%(1)
etc...

A bit unhandy if you have more than 2 or 3 arrays to manipulate. The is where userdefined datatypes enters the scene. These datatypes can contain several different kinds of datatypes all in one element, letting you easily handle stacks of data:

TYPE variables
  A AS INTEGER
  B AS INTEGER
  C AS INTEGER
END TYPE

DIM TestArray AS variables

TestArray(2) = TestArray(1)

The last line of code performed the exact same action as the 3 lines of code in the previous example, pretty slick huh? This can save you a LOT of time and bother and will even make your code look much neater. Building a usertype like this involves three steps: finding out what kind of data you need, making a type for it and allocating memory for that type. Pretty simple. The TYPE statement that defines the usertype works like this:

TYPE TypeName
  Element1 AS Type
  Element2 AS Type
  .....
END TYPE

TYPE is the actual QBasic statement, followed by the name of the datatype you're creating. Next comes a list of all the elements in your datatype, these elements can be:
 
INTEGER A 16-bit signed integer variable
LONG A 32-bit signed integer variable.
SINGLE A single-precision 32-bit floating-point variable.
DOUBLE A double-precision 64-bit floating-point variable.
STRING n%  A fixed-length string variable n% bytes long.

And yes, you can have different types of data in your type, all types of data will fit together in one type, great eh? This feature of QBasic can be handy in a lot of situations that require moving and rearranging large amounts of data, like in our database program. So guess what? We're going to create a usertype for our database right now. As it's an adress database we will of course want to store a persons name, addresse, city/state and an optional phone number. Our datatype:

TYPE RecordType
  Names AS STRING * 25
  Address AS STRING * 25
  City AS STRING * 25
  PhoneNr AS STRING * 10
END TYPE

We could also have added other elements such as Integers for peoples ages, but since most of your friends probably won't tell you how old they are we'll leave this one out. So now we have a usertype for all our addresses. Next step is to allocate data for the usertype. It's not a variable until we take this step. We use the DIM statement for this (most people think DIM is only for arrays, don't listen to them):

DIM Record AS RecordType

And voila! We now have a fully functional variable called Record to use in our program, but the smart-alec reader will have noticed that we have four elements in a variable with only one name. Correct, to get around this we add a period (.) after Record followed by the name of the field we want to access, and we have a regular variable: Record.Names is the name of the first field, Record.Address is the second etc. That's all there is to know about usertypes. You will notice when reading the source code of the example program (SPEX10.BAS) that a whole record holding a persons name, address, city and phone number can be written to a random access file with one statement, beats using conventional arrays doesn't it?

4.0 Using loops
Loops stick the routines of your source together to a working program. It is important to learn how to handle these loops in an efficient manner, as well-structured loops make your program work better. There are several kinds of loops available to us in QBasic, most of which you probably know about already, but following this I will try to explain the in's and out's of all the looping techniques you need in your programs.

4.1 DO .... LOOP
This looping method wasn't possible in early versions of BASIC that required line numbers, so it is a fairly new addition to the language. This kind of loop lets you handle conditional jumps in a very efficient manner, as you can either add your condition to the DO or the LOOP statement. This first example checks the status of the keyboard string before it even starts the loop, so the loop may not even be executed once if a key has been pressed prior to the routine:

DO UNTIL INKEY$ <> ""
PRINT "No key pressed"
LOOP

On the other hand, if you need your loop executed at least once you can add the condition to the LOOP statement:

DO
PRINT "No key pressed"
LOOP UNTIL INKEY$ <> ""

Get the general picture? This is however not the only way of using the DO...LOOP method, you can also use boolean (true/false) operators to control your condition. Inserting AND between conditions will only interfere with the loop if all the conditions are true (non-zero), using OR will react when either one of your conditions is true:

DO UNTIL INKEY$ <> "" AND A% > 0
 - This will exit the loop only when both
   conditions after the UNTIL keyword are
   true (non-zero)

DO UNTIL INKEY$ <> "" OR A% > 0
 - This will exit the loop when either one
   of the statements are true.

There is another keyword you can add to your condition, which is NOT. This word reverses the meaning of the following boolean statement... confused? I know I am, but let's try with an example:

DO UNTIL NOT INKEY$ <> ""

This breaks the loop when a key IS NOT pressed, e.g. when the conditional statement is false rather than true. You can use several NOTs in your condition:

DO UNTIL NOT INKEY$ <> "" AND NOT A% > 0

This will break the loop if no key is pressed and the variable a% is less than or equal to zero. Alternatively, you can completely leave out the condition at the front or end of the loop, and use EXIT DO within the loop to terminate the routine:

DO
IF INKEY$ <> "" THEN EXIT DO
PRINT "No key pressed"
LOOP

I know all this must sound confusing, but then again you're not supposed to learn everything from this tutorial. Once you have tried using the different loops and conditions it will all make perfect sense to you, I promise.

4.2 FOR ... NEXT
Most people starting off in QBasic have made that great program that prints your name all over the screen 100 times, haven't you? If you did you probably used a FOR ... NEXT loop, which is what we'll discuss here. This kind of loop is what you use when you need a routine executed a fixed amount of times (e.g. printing your name all over the screen). The syntax is very simple:

FOR Variable% = LowerBound TO UpperBound [STEP Difference]
....
NEXT Variable%

Variable% is the variable that will be increased or decreased every time the interpreter reaches the NEXT statement. Simple eh? For the names program it works like this:

FOR a% = 1 TO 100
  PRINT "Your Name Here"
NEXT a%

This example doesn't actually use the variable a% in it's operation, so it's not necessary to do anything fancy like STEPping. The routine will just loop one hundred times and pass control to the next statement. If, on the other hand, you want to use this loop for something constructive, you might need to use the STEP keyword. This let's you increase the variable by more than one at every loop, or even make the counting go backwards. Let's say we want to print a dot at every fifth position on the screen:

FOR Dot% = 1 TO 80 STEP 5
  LOCATE 1, Dot%
  PRINT "."
NEXT Dot%

That example is of course completely useless, but illustrates how to use the STEP keyword. If you want to decrease your variable on every count, STEP must be followed by a negative number and the low bound must be greater than the high bound, for example...

FOR Dot% = 80 TO 1 STEP -5
  LOCATE 1, Dot%
  PRINT "."
NEXT Dot%

Another useless example that just prints the same dots on the screen starting from the right instead of the left. But what if you want to fill the whole screen with dots? Unlikely, but let's say you do. Writing 25 statements, one for each row, would take some time to accomplish, which is why we can do nested loops:

FOR Row% = 1 TO 25
  FOR Col% = 1 TO 80
    LOCATE Row%, Col%
    PRINT "."
  NEXT Col%
Next Row%

This would execute the inner loop 25 times, one time for each row on the screen. Beats copying and pasting the loop 25 times eh? You can nest as many loops as you like, but keep in mind that QBasic can be rather slow in it's calculations. The two loops in the example alone cause the interpreter to loop a total of 25*80 = 2000 times, nesting an additional loop of 15 would make a total of 30.000 loops, and quite frankly that just wouldn't work. This is also a part of structured programming, avoiding unnecessary loops and moving time-critical operations outside of loops, but this will all come clear to you when you try to calculate a sine value 2000 times in a loop. (Ok, so we could have used the STRING$ statement instead of looping an additional 80 times, but it's just an example, right? :)

You may also have noticed that the examples have extra blank spaces on the left every time a new loop is started, this is not necessary but really makes your code much easier to read, and not doing this will generally cause programmers to frown at you, so make it a habbit!

5.0  Conditions and branching
Without conditions your program would output the exact same data every time it was run, pretty boring, but fortunately we have two methods of branching off to subroutines in our programs, making them a little more fun to work with. Both methods have their advantages and disadvantages, so consider which one you'll use in your program, it's a part of structured programming!

5.1  IF .... THEN logic
You may have worked with this kind of condiditional branching previously, it is quite simple and effective. Basically, using IF THEN logic will cause your program to branch off to a subroutine when a condition is met, and another if the condition is not met. This is IF THEN in it's simplest form, and works like this:

IF Condition THEN .......

The condition works the same way as in DO LOOP loops (chapter 4.1). If the condition is met, the statement after the THEN keyword is executed. Simple. But what if it is not met? We could add a subsequent IF THEN statement with the opposite condition, telling the program what to do in that case. Or even better we can add the ELSE keyword to the existing statement:

IF Condition THEN ......... ELSE ......

The statement after the ELSE keyword will be executed if the condition is not met. Now only one problem remains, what if we want to branch off to an entire subroutine instead of just one statement? For this we have block IF THEN's:

IF Condition1 THEN
  ......
ELSEIF Condition2
  ......
ELSE
  ......
END IF

These work basically the same way as the basic IF THEN statement, except you can type several lines of code for each condition. The ELSEIF keyword lets you specify multiple conditions in the same IF THEN block, handy for things like menuhandling:

IF Choice% = 1 THEN
  { Subroutine for #1 here }
ELSEIF Choice% = 2 THEN
  { Subroutine for #2 here }
ELSEIF .......
ELSE
  { Subroutine for invalid choice here }
END IF

This brings us on to the next conditional jump method, CASE....

5.2  SELECT CASE
This is the modern and trendy version of IF THEN statements. This method offers some advantages over IF THEN as you will discover in a short while. The syntax for SELECT CASE is:

SELECT CASE Variable%
  CASE Comparison1%
     .......
     .......
  CASE Comparison2%
     .......
     .......
  CASE ELSE
     .......
     .......
END SELECT

Variable% is of course the variable you will be handling in your conditions. After each CASE keyword, a string or variable is placed that is compared to the Variable%. If these two match, the routine after the CASE keyword is executed. You can even specify several matches for the same routine, for example you could make "A" and "a" do the same thing, which would also be pretty handy for a letter-operated menusystem. This is done by seperating the comparison variables with commas:

  CASE "A", "a"
    .......

Pretty simple too when you get the hang of it. Again, additional spaces have been added to the left of the CASE keywords to make the code easy to read and understand. SELECT CASE is typically used when there are several possible subroutines that can be branched off to, it's simply more convenient than a huge block of IF THEN's. And convenience is what we're looking for.

6.0  Your comments please
Everybody knows what they are, but nobody uses them when they should. The use of comments in your source can really speed up the understanding of the code. At a glance you can see what all your routines do without having to understand all the calculations and variables used within, pretty slick eh? But then again we don't want to overdo it, placing a comment after a PRINT statement saying 'This prints the text blablabla to the screen' is just a waste of time. On the other hand, the following statement might need some explaining:

 a = (SIN(A1) * c) / SIN(C1)

When confronted with a statement like this you'd have to look back in the program to find out what the variables represent, and then take a look at the calculation itself to find out what the program is doing to them. Adding a few comments, however, might ease the pain a little...

' Following routine calculates the side A of a triangle ABC, using
' the side c and the angles A1 and C1. Angles A1 and C1 are 124 and 10
' respectively, side c is 13...
 

 a = (SIN(A1) * c) / SIN(C1)    ' Calculate size of side a

The example wouldn't have been too hard to figure out without comments, but routines consisting of several pages of code tend to get a bit harder, so use comments when something needs explaining, other programmers will respect you for it.

7.0  Name your Variables
This is a very short but important chapter. As you might have guessed, it's all about naming your variables in a way that makes your code easier to understand to yourself and others. We get to use as many characters as we like (almost) in our variable names, but too much of anything isn't good for the baby, the variable MainLoopIterationCounter is a legal variable, but it takes time to type and is prone to misspelling. You get the idea. Something like MainLoop would be sufficient and understandable.

You can also add little signs to the end of your variables indicating what type of variables they are. As mentioned earlier, there are five types of variables, and five corresponding signs:
 
 
Integers %
Long Integers &
Single Precision !
Double Precision #
Strings $

So, the variable a% is an integer, a! a long integer etc. etc. Using these signs saves memory and can make program execution faster, so by all means get used to it, it's a good programming habit.

8.0  Closing words
Naturally you won't be a perfect programmer after reading this, it takes days and weeks and months to learn how to structure your programs in the most convenient manner, but throughout the document you've been introduced to several important techniques and methods that could make programming easier and a lot more fun in the future. I hope you enjoyed reading this as much as I enjoyed typing it, please feel free to contact me if you have any questions regarding anything with QBasic on dtordrup@mail1.stofanet.dk. Thanks to Larry (nonnypa@gte.net) for helping out with the testing of this tutorial, without his advice and constructive criticism it would be even more confusing than it is now ;)



This article originally appeared in The BASIX Fanzine Issue 15 from June 1999.