Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

Newcomers DBPro Corner / Beginners Guide To Programming - Part II Layout, Structure And Style

Author
Message
TDK
Retired Moderator
22
Years of Service
User Offline
Joined: 19th Nov 2002
Location: UK
Posted: 21st Dec 2006 01:09 Edited at: 8th Feb 2008 15:08
TDK_Man's Dark Basic Programming For Beginners


Part 2 - Layout, Structure And Style


Before I start with the tutorial, how did you get on with the programming task I set at the end of Part 1?

If you got a working program, then well done. Below is how I would have done it. In a program this minor, there is little difference between any of the methods you use, so if you did it differently, then no problem.

The method I have used is not meant to be the best or definitive way to do it - it's just the way I decided to do it. Who knows, there may be reasons why your method is better than mine!

Anyway, here's my solution to the task…



Only a couple of things worth mentioning...

First of all, notice that the variables Best and Worst are initialised at 1000 and 0 repectively which seems odd.

Whenever you are recording the highest and lowest of something you you should always start the variables off at the opposite end of the scale. Here, lower number guesses are best, so we start the variable Best off at 1000. That way, when the player has finished the first game and has taken less than 1000 guesses (pretty likely), then Best is replaced with that number. In subsequent games, Best is replaced by the number of guesses ONLY if it is less that the number already stored in the variable Best.

Worst works in the same way only in reverse. The first game played, the 0 in the variable Worst is replaced by the number of guesses made and subsequent games will only be replaced again if more guesses are taken.

By the end of the game, the least number of guesses will be stored in Best and the most in Worst.


OK, so now on with Part 2 of the tutorial series…

If you remember at the end of the last tutorial I gave you two small examples of Dark BASIC code to demonstrate the use of variables, at the same time, pointing out that they weren't much use as they had to be re-run each time you wanted to use them. This is particularly relevant with the second example as it would be better if we could keep re-entering different temperatures and only end the program when we are finished with it. This is actually quite simple to do, but before we do it's important to know why it ends so abruptly.

When all DB programs are run, the computer executes each line of instructions starting with the very first line. It then carries on with the next line in sequence continuing until there are no more lines to execute or it reaches the command END.

At any time during the execution of your program, something called the Program Counter keeps track of the current line and unless the instruction on it tells the program counter to jump to a different line, it drops down to the next one and continues. When there are no more lines, the program ends.

It's like reading a book. You start with the first line on page one and when you have read the last line on the last page the book is finished.

Let's remind ourselves of the last example program from tutorial 1:


Print "Please Enter Temperature In Centigrade: ";
Input C
F = 1.8*C+32
Print C;" degrees centigrade equals "; F;" degrees Fahrenheit."


Here, the message asking you to enter a value is printed to the screen, the computer stops and waits for input on the next line. When the user enters a value and presses the Enter key, the next line calculates the result and on the final line, the result is printed to the screen. As there are no more lines, at this point the program has nothing more to do so it ends.

There are however commands which you can use to control the order in which the lines of instructions are carried out. Our example above doesn't use any of them so the program just zips through all the lines in order and then ends.

GOTO

One such command is GOTO which tells the program counter to jump to a specific location in the program, skipping all lines in between. This can be a point anywhere in the program but has to be a defined label.

A label is a single word ending with a colon. The rules for naming labels are the same as for variables. Here's an example:


Print "Program started…"
GOTO Label1
Print "This will never be printed!"
Label1:
Print "This line appears!"


If you enter this program in and run it, you will see that the 'Program started' message is printed to the screen followed by the 'This line appears!' message. The 'This will never be printed!' message never appears because on the second line, the program counter is told to jump to line four, skipping line three.

This is the crudest method of controlling program execution and should be avoided whenever possible as it is possible to create what is known as 'spaghetti code' where control leaps around your program, making it very difficult to follow when your program grows in size.

Continuing with our book analogy, imagine if at the bottom of page 1 it said 'continues on page 96'. You turn to page 96 and continue reading only to find at the bottom of page 96 that it says 'continues on page 12' and so on. In theory you might never finish the book - and that's what keeps a computer program from ending.

Loops

Sometimes, you want your code to do something a certain number of times and repeating the lines isn't a viable solution. As a silly example, you may want to print three times, the sentence 'This is printed three times.'

Obviously, this can be done with three lines - one for each time you want the sentence to appear:


Print "This is printed three times."
Print "This is printed three times."
Print "This is printed three times."


But, what about printing something 20 or even 100 times. As you can imagine, this would involve a lot of typing. So, to make life easier, this is where control loops come in.

There are many types of loop - all of which could be used to print our lines, but for this example the best one to use is the For…Next loop because we know how many times we want to print the sentence.



This loop uses a variable to count from the first supplied number to the last - in this case from 1 to 20 using the counter variable N. You can use any variable name for the counter variable as long as the name used on the Next line and the For line are the same.

The first time the For N= line is encountered, the variable N is set to the first value (1) and the line number is recorded.

On the next line, the text is printed. When the Next N line is reached, the value of N is incremented and compared with the end value (20).

If it is less than the end value then the program counter jumps back up to the line number recorded earlier and the loop is repeated. This continues until the value of the loop counting variable N matches the end value, at which point the loop is ended and program control drops down to the line following the Next N line.

In the process, the above loop will print the text message to the screen 20 times. Obviously you can have as many lines between the For and the Next lines - they will all be carried out the stated number of times.

For…Next loops can be used to count from any number to any other number - you don't have to start at 1. You don't even have to count in increments of 1 either as there is an optional STEP parameter that can be used on the end. This provides the increment for the counting loop (which defaults to 1 when Step is not used).

For example: For N=1 to 100 Step 10 will start with N equalling 1, but the next time around the loop, 10 will be added making N equal 11. The next time it will be 21 and so on up to 91.

Notice that the highest value it reaches is 91. This is because adding another 10 to 91 would take it over 100 - the stated end value, so when it can't add any more the loop is ended. Remember this as it might save you a few headaches in the future!

Hint: If you wanted it to count 10, 20, 30 etc up to 100 you would use For N=10 to 100 step 10. (Starting at 0 instead of 10 would give you 0, 10, 20 and so on).

Finally, there is the ability to count backwards. This is simply done by making the start number higher than the end number and using Step -1:

For N=20 to 1 step -1 will count from 20 down to 1.

Note: The Step -1 is required for counting backwards - even if you are counting down 1 number at a time. The default when not using Step is positive 1 regardless of the start and end values!

Usage Notes:

This type of loop is ALWAYS carried out the stated number of times (unless a run-time error occurs inside the loop). The counter variable always equals the whole range of values from the declared start and end range inclusive (unless Step is used). This makes it safe to use the counting variable for other things inside the loop.

In the process of a For…Next loop, the value of the variable used as a counter will change. For this reason, do not use names which have been used elsewhere in your program. As a tip, I use N, N1, N2, N3 etc ONLY as For…Next counting variables and do not use them anywhere else in my programs.


Other Loops:

As mentioned previously, there are other types of loop and printing a message a number of times could be done with any of them, so let's take a look at them:

Do…Loop

The Do…Loop is the most basic of the loops in DB and has no conditions so isn't normally exited from - you would usually exit the program with the END command without leaving the loop.

Program control will go around one of these loops forever so it is primarily used to create an enclosed main program loop in your program. This is covered later on. For now, it's enough to know that we would not use this type of loop to do the above example task.

Repeat…Until

The Repeat…Until loop is exited from only when the condition on the Until line is met. Unlike the For…Next loop, somewhere in this loop variables must be altered in order for the condition to be met.

Let's see an example based on the above For…Next loop example:



In this example, you can see the condition on the end. A condition is basically a test which will return 0 (false) or 1 (true). In this case the condition for exiting the loop is that the variable Counter must equal 21. The program counter will go around in a loop forever if Counter never equals 21 so that's why we set the variable to 1 before entering the loop and increment the variable each time we go around the loop with Inc Counter. (Inc is short for increment and adds 1 to the named variable).

Usage Notes:

As the counter starts at 1 and the counting variable is incremented at the end of the loop (after the Print line), the variable will be incremented to 21 after the line has been printed 20 times. That's why the exit condition variable has to be equal to 21 - after which the loop is immediately exited and the Print line not executed a 21st time.

As this loop is repeated until a specified condition is met and the condition is tested for at the end, the lines in this loop will always be carried out a minimum of once - regardless of whether the condition is true or false before entering the loop.


While…EndWhile

This loop is essentially the Repeat…Until loop with the condition at the start of the loop instead of the end.



While even having this type of loop may not seem worth the bother, already having Repeat…Until, there is one subtle but significant difference. As the condition is tested at the beginning of the loop, if the condition fails then none of the code is executed - unlike Repeat…Until which has to carry out the code once before the condition is tested for at the end.

Usage Notes:

The code in the loop is only executed if the counter variable is less than 21.

It's important to realise that this loop is not entered at all if the specified condition is not met at the start of the loop, as such, the lines inside this loop may never be carried out at all.

So, back to the original theme - stopping our little temperature program ending until we want it to...

All we need to do is add a main Do…Loop enclosing the existing code and add a condition to allow the user to exit. The final program might look like this:



Notice that as mentioned previously, the main Do...Loop is never actually exited from - it's main use is to stop the program from ending until we are ready. This is done with the End command when the user says no to converting another temperature.

OK, that's loops covered, but before we cover any more BASIC commands and get too bogged down with them, let's take a look at how best to put them into a program that will work with optimum performance and still be legible.


Program Layout

The worst thing you can have to do is try and fix a fault in a program which has been poorly laid out, has Goto's everywhere leaping all over the place and has no structure. It doesn't help if it's your own code either!

So, while you are a newcomer, it's best to get into the habit of writing your programs neatly now.

Writing programs in DB or any other language is finicky - as no doubt you have already discovered. This is because of something called syntax.

When you write a paragraph of text in a word processor for a friend to read, all your speeling mystaykes make your text look bad, but your friend can still read it and understand what you mean.

Unfortunately, a computer even these days is nowhere near as powerful as the human brain and has no way to decipher what you meant when you put ForA = 1 To 10 or put Repet instead of Repeat.

This means that you have to type everything EXACTLY as it is supposed to be - right down to every full stop, comma or semi-colon being in the right place.

Spaces too are also important. Too many isn't usually a problem, but leave one out when it should be there and your program line will not be recognised.

Luckily, DB will stop you running a faulty program and highlight the iffy line - even though it sometimes can't tell you exactly what is wrong with it!


Indentation:

Few people indent their code. It takes time and doesn't make your programs run any faster so why bother? And what is indentation anyway?

Indentation is the offsetting of lines of code in the editor. Indenting makes your code a LOT easier to follow and thus bugs are easier to find - especially when you have more than a couple of hundred lines. You may have noticed that all of the examples in the tutorials so far all use indentation - the code inside loops is offset from the rest of the code.

One of the more common errors in programs by new programmers is failing to close loops. Your program may have 20 Repeat lines but only 19 Until lines and once you have a decent sized program, these can be difficult to find - if you don't use indentation...

As a rule of thumb, I always increase the indentation of the code inside every loop by two spaces and drop back to the left at the end of the loop. Some people use three spaces, but I find that with a few nested loops (loops within loops) all but the shortest lines of code are off the right side of the screen!

I have always indented Open File lines too as they have to be closed and are similar to loops in that respect.

An indentation example (not real code):



As you can see, indentation makes all the loops stand out so missing Next, Until and EndWhile lines are easier to pick up.


Layout:

As well as indentation, you should also adopt a suitable layout for your programs. Once again, it doesn't always make them run any quicker (though it can), but program development time and more importantly error tracking time can be reduced dramatically.

I have always said to people who have asked me how long it would take to write an application for them, "two months to write it, six months to get it working properly and forever to get rid of all the bugs". Although a joke, this isn't too far from the truth, so anything you can do to help along the last one is a bonus.

Finally, a proper layout for your programs will make them smaller and more efficient. Useless or repeated code can be avoided and modifications are made a lot easier. So what does this involve?

First of all, your programs should have the following format:


* Initialisation
* Game Menu
* Main Program Do…Loop
* End Of Program
* Subroutines
* Functions


What Are Subroutines (sometimes called Procedures):

These are vital to keeping your programs running smoothly. You can write programs without them, but once you have used them, you wouldn't dream of going back to being without them!

Basically, you can think of a subroutine as a little stand-alone program which you can call on at any time. The code in a subroutine is something that can be called many times in your program - and hence prevents you having to retype the code each time you need it.

If you needed to print ten lines of text in three different places in your program, rather than have thirty lines of code, you would put the ten lines into a subroutine and use a single line of code to call the subroutine whenever you needed the ten lines printed.

Alternatively a subroutine can simply be code that you may only need once, but just want to keep in a separate location making your program tidier.

Subroutines start with a label and end with the line Return. The command GOSUB is used to call the subroutine and as the calling line is stored, the Return bit at the end knows where to jump back to after the code in the subroutine has been executed. For this reason, you should never exit out of a subroutine by using GOTO, though calling another subroutine from a subroutine is OK as it too will return automatically.

On return from a subroutine, control is passed to the line immediately after the GOSUB line.

Although a subroutine is a separate block of code to the code in the main program Do…Loop, it is still classed as part of your main program. As such, all variables in your main Do…Loop are available in a subroutine and any alterations made to them in the subroutine are seen by your main program.

Having now covered subroutines, you now know enough to continue with our list above (if you were wondering why I didn't start with item 1 - Initialisation).


Initialisation

At the start of your program, you need to initialise all the required variables, set the screen mode, maybe create a matrix, load images and objects, turn off the mouse if necessary and all those sort of tasks. I put all of these into a subroutine called Setup. Your program then only needs a single line which says GOSUB SETUP at the start to set everything up rather than having all that code cluttering up the start of your program.


Main Menu

Next, you may want your program to display a menu screen with buttons for things like Options, 1 Player Game, 2 Player Game, Start Game and Exit - rather than just starting the game.

As you need to call this menu at the end of each game, you should place it in a procedure.

Inside the procedure you should have a conditional loop like Repeat..Until which is only exited if the user clicks on one of the Start Game buttons.

At the end of any game, you can simply call this procedure again with Gosub to display the menu screen again.


Main Program Do…Loop

Your main program should be enclosed in a Do…Loop so that when it runs, it doesn't end until you want it to. Some coders prefer to use Repeat...Until for the main loop. Either will do - it's a matter of personal preference.

When the time comes for it to end, you can use the END statement inside a condition along the lines of 'if the user presses the X key then end the program'.

From within the main program loop, subroutines can be called and they will always return back to continue round the loop.

If you are writing a 3D program, then the last line inside the loop should be a Sync. More will be said about Sync later in the tutorials, but for now all you need to know is that in most 3D programs, for optimum speed, you will want to control the screen output yourself and issuing a Sync updates all the 3D information on the screen. Each Sync takes time, so one placed at the end of the main loop refreshes the screen with optimal performance.


End Of Program

This is simply the word END which stops your program from running. Placed on the line following the LOOP line, it should never be executed as there is no way to get out of the Do…Loop to even reach it.

However, if program control ever reached this point, then it would crash through any code which follows - something you don't want as it would cause an error if all your subroutines follow.

So, putting it in doesn't do any harm - I do it out of habit…


Subroutines

This is the place in your programs where you put all your subroutines, one after another. I tend to put a Rem line immediately before each subroutine with a brief description of what it does.

Note: Rem is short for REMark and is purely a comment. Use them anywhere in your program to leave yourself 'notes' on what a section of code does or as a reminder to return later and alter something. Remarks have no overhead on your programs as they are completely ignored when your program is compiled - they only exist in the editor!

So, a program with tons of comments in the editor will have exactly the same size compiled exe as the same program with none so USE THEM! Load a program written months ago and you'd be surprised how little you will recognise - even if you did write it yourself. Lots of comments will remind you of what does what and you'll be glad you added them.


Functions (In brief):

I'm not going to cover functions in great depth here as there are more detailed tutorials on the subject available.

Functions are for all intents and purposes the same as subroutines but with a couple of differences.

They are blocks of code like subroutines and after being used return to the line after the one they were called from - just like subroutines. But, functions start not with a label but with the word FUNCTION followed by the function name and an optional parameter list in parenthesis which combined is called the function header.

Functions end with the word ENDFUNCTION and an optional variable which is used to pass data back to the calling line. The calling line uses the function name and must include a list of parameters identical to the function header (if any parameters are used).

This sounds quite complicated so let's see an example:



OK, this aptly named function receives integer values which it places into the numeric variables A and B, and a single string which it puts into the string variable A$. It calculates the length of the supplied string using the Len() function and stores it in the variable L.

Next, it multiplies the value of L with the value passed to it in A and adds the value in B, storing the result in the variable C. Finally, the function ends and passes the value stored in C back to the calling line.

So, an example of this function being called might be:

NumVar=Pointless(10,5,"Hello")

As the function returns a value, we have to use a variable to 'catch' what is returned - hence the NumVar= but at the start. If the function returned no value, this bit could be omitted making the calling line Pointless(10,5,"Hello").

Next comes the function name and the parameter list in parenthesis. When the three items get to the function they are placed in A, B and A$ respectively. We are passing proper numbers and a literal string here, but we could have used variables instead. If we did, the variable names would be irrelevant, but the variable types must match. The same goes for the return variable type too.

The only restriction is that you cannot pass arrays to a function.

In the function, L becomes 5 (the length of the word Hello), which is multiplied by A (10) and has B (5) added to it. The result (55) is returned by the EndFunction and is caught be the variable NumVar.

Note however that only a single item of data can be returned from a function. That's one numeric variable or one string - no multiple variable lists like the entry parameters.

Another important difference with functions is that they use local variables (see subject Scope below). Variable names inside functions can be the same as those outside the function but still be completely separate entities which can co-exist together. Local variables are destroyed when exiting the function.

Before we go any further, I think it would be best to spend a few moments covering local variables and the other type - global variables. I didn't cover them in the variables section in Part 1 of this series as they are only of any relevance when you know what functions are, so I left it until now to cover the subject.


Scope

In non-technical terms, scope is where in your programs your variables can be seen. In many programming languages you have both Local and Global variables which are declared before you use them. Where you declare them affects where you can see them.

Global variables can be seen anywhere in your program. This sounds a bit obvious until you realise that Local variables are local only to functions - they cannot be seen outside of the function that they are used in.


Local Variables:

DB Classic is slightly different to most programming languages as global variables don't officially exist. For example, if in your main program you say A=10 then A can be seen throughout your program - including procedures but excluding functions.

Inside a function, you can also have A=100 but you have to remember that this A is local and is an entirely separate A to the one outside the function - in your main program. When you exit from the function, the function's A variable is destroyed and you will find that the original A still contains 10 - not 100!

Likewise, if in your main program you had B=100, when you enter a function, B does not exist, so you can't use it! That's why you have the parameter list in the function header so you can pass the variables you do need to the function. For the same reason you have the variable to use with EndFunction so you can pass the result from the function back to your main program.

This is what local variables are all about.


Global Variables:

OK, earlier I said that in DB, global variables don't officially exist. Well they don't, but there is a work-around you can use if necessary.

It turns out that arrays declared using DIM in your main program are sort of global in so much as you can see them inside functions, alter their contents inside the function and the alterations are intact in your main program when you exit the function!

This gives us a very nifty way around the fact that you can only pass a single variable back from a function. With arrays we have no need to pass more than one as we can use arrays.

Arrays declared inside a function however are still local and are destroyed on exit. They cannot be seen on return to the main program. Also, as they are destroyed when exiting the function, you can DIM an array at the start of a function and call it many times without getting an 'Array Already Dimensioned' error. You don't have to 'Undim' as you would in the main program.

This also conveniently gets around the limitation mentioned earlier that you cannot have an array in the parameter list you pass to a function. Now you don't need to as the array can be seen and modified inside the function anyway!

And that's program layout covered. Our skeleton program layout should therefore look something like this (not real code - layout example only):



If you use this sort of layout, you should find programming a lot more fun than if you just bang out the code all over the place - especially if it doesn't work when you hit that F5 key…

When you add another feature to your program, you simply put it in another subroutine in the subroutine section and add the calling line into the main program loop. Doing this keeps the code in the main loop as short as possible and easier to follow.

That's it for Part 2 of the Programming For Beginners tutorial series.

TDK_Man

Xenocythe
19
Years of Service
User Offline
Joined: 26th May 2005
Location: You Essay.
Posted: 21st Dec 2006 01:11
Good lord

These tutorials are amazing! Incredible work!

-Mansoor Siddiquie

Login to post a reply

Server time is: 2024-11-26 05:27:13
Your offset time is: 2024-11-26 05:27:13