Chapter 15 - Grouping Data in Structures
Arrays are a great way to organize data. In your programs you will come
across numerous uses for them. Eventually, you may happen upon a
situation like the following. Suppose you are writing a killer game.
The player has several variables associated with him: x-coordinate,
y-coordinate, number of lives and name. You could easily define several
variables like this:
PlayerX%=5 |
PlayerY%=12 |
PlayerLives%=3 |
PlayerName$="GR8" |
Or you could use an array for the first three variables and make a
mental note that index 1 refers to x position, 2 to y position and 3 to
lives. PlayerName is a problem and would have to remain an individual string.
BBC BASIC offers the ability to create your own 'super' variable which
will group all these individual variables together in one place and
hence make them easier to keep track of. This device is called a
structure as you probably guessed from the title of this section. To
declare a structure, you use DIM, like an array but with curly brackets (braces)
instead of the normal round ones so BASIC knows that we're talking
about a structure. Within the braces go the names of the variables
that we wish to include in that group. Usual naming rules apply. Let's
see an example:
DIM
Player{X%,Y%,Lives%,Name$} |
We can access and use any of the variables and do all the normal things
with them. The structure only acts as a grouping mechanism. Access is
done by using the name of structure (Player in our case) followed by
a dot and the name of the variable we want. To change the number of
lives when a player gets hit, we would do this:
Player.Lives%=Player.Lives%-1 |
IF
Player.Lives%<=0 THEN
PRINT "GAME
OVER" |
The official jargon here is:
So now you can declare nice little units to hold all your variables in
logical groups. But there's more. You knew I was going to say that,
didn't you? The members of a structure are not limited to single
variables. You can have an array as a member of a structure. Suppose
your player could have up to 10 items of equipment: underwater flame
thrower, anti-gravity boots etc. You need a way to represent
whether he's picked up each of these items. Let's have an array
called Item%. This is how we would declare the structure now:
DIM
Player{X%,Y%,Lives%,Name$, Item%(10)} |
Access is just as before:
IF
Player.Item%(5)=TRUE
THEN |
PRINT "You
have the 1000 league boots" |
ENDIF |
Similarly, you can have another structure as a member of the structure.
For example, it is common practice to make a structure to hold the X and Y coordinates.
You do it like this:
DIM
Player{Posn{X%,Y%},Lives%, \ |
\ Name$, Item%(10)} |
Posn (for position) is referred to as a substructure and accesses to its
components are like this:
Player.Posn.X%
= Player.Posn.X% + 5 |
Substructures can contain other substructures, arrays and variables in
any combination. The idea here, of course, is to divide the data up
into logical, easy to manipulate groups, not to show how clever you are
by nesting 10 levels of data inside 10 levels of data.
Arrays of Structures
When our player has reached the end of the game, victorious and covered
in gore, we would like to add a high score table so he can brag to his
friends. The table will consist of the top 10 scores. By now, it should
be apparent how this is done:
Great, but that only takes care of the top position, we want a table. This is how we
declare an array of structures:
DIM
Table{(10) Name$,Score%} |
The first item in the variable list is the number of elements in round brackets
(parentheses). This will give us an array called Table with 11
structures 0 to 10. To print out the contents of the high score table,
we access the array like this:
FOR I% = 1 TO 10 |
PRINT
I%,Table{(I%)}.Name$; |
PRINT Table{(I%)}.Score% |
NEXT I% |
You need to wrap the array index in braces to tell BASIC you're
working with a structure. As with normal arrays, multi-dimensional
arrays are allowed and the same rule about numbering from element zero
applies.
Prototype Structures
Back in our hypothetical game we have a structure to hold the aliens:
current position, health points etc. At the end of the level there is
the tough-as-old-boots-end-of-level boss that you have to defeat before
winning through to the next level. This chap has the same attributes as
an ordinary alien, but it doesn't really make sense to include him with
the rank and file. We need to create another structure for him which
has the same attributes as his underlings, but is kept separate so we
can deal with him as a unique case. BBC BASIC allows us to use an
already defined structure as the template or prototype for another one.
REM
Prototype structures |
DIM
Alien{XPosn%, YPosn%, Health%}
|
|
REM
Setup our alien |
Alien.XPosn%=50 |
Alien.YPosn%=627 |
Alien.Health%=100
|
|
DIM
BigBadBoss{}=Alien{} |
REM
Let's play ... |
BigBadBoss will now have the members:
BigBadBoss.XPosn% |
BigBadBoss.YPosn% |
BigBadBoss.Health% |
Don't get confused here. At the point when the structure is declared,
all the members of BigBadBoss will be set to zero, it is only the names
of the structure members that are copied over, not their actual values.
Using this technique, it is also possible to declare an array of the
specified structure.
REM
Arrays of prototype structures |
DIM
Alien{XPosn%, YPosn%, Health%}
|
|
REM
Declare a full level of aliens |
DIM
LotsOfAliens{(20)}=Alien{} |
LotsOfAliens{(0)}.XPosn%=50 |
LotsOfAliens{(0)}.YPosn%=627 |
LotsOfAliens{(0)}.Health%=100
|
|
REM
... |
Line 5 gives us 21 (0 to 20) of the little blighters ripe for
decimation.
In one of the previous sections, we had a player structure which had a
position structure nested within it. This technique can be applied to
prototypes as well. As an example, we can define the position of our
alien using XPosn% and YPosn%, but we could make this a structure too.
That way each time we had something that needed a position, the
prototype would give it to us ready-made. This is useful, because it
means we are always able to access the position in the same manner,
wherever it is found. This is how we would do that:
REM
Nested prototype structures |
DIM
Position{X%,Y%} |
DIM
Alien{Posn{}=Position{}, Health%} |
DIM
Player{Posn{}=Position{}, \ |
\ Lives%, Name$, Item%(10)} |
|
REM
Initialize our alien |
Alien.Posn.X%=50 |
Alien.Posn.Y%=627 |
Alien.Health%=100 |
|
REM
Initialize our player |
Player.Posn.X%=50 |
Player.Posn.Y%=627 |
Player.Lives%=100 |
|
REM
... |
Any changes made to the Position structure after the initial coding
would instantly be reflected in the changes to the structures that use
Position as a prototype. This is good and bad. If the game was to be
expanded into three dimensions, we would redeclare like this:
and presto, both Alien and Player now have another dimension to play
in. (You still have to write the code to handle that yourself. BBC BASIC
isn't THAT clever.) The point to be wary of is going the other way, if
you remove an element, you must also remove every reference to it in
every structure that contains it. Think about the layout of your data
before implementing it, this way you save hours of frustration and
rework.
Obviously, these techniques come into their own when using larger
structures, but don't ever lose sight of the fact that they are merely
ways of collecting similar data together so it's all accessible from
the same place. Once you get the hang of structures, you'll wonder how
you ever managed without them.
Tip: Structure names and abbreviated keywords |
BBC BASIC allows
the use of abbreviated names for most keywords and commands. These can
be found under the entries for each word in the help files. As an
example PRINT can be shortened to 'P.' - see the problem? If you
declare a structure called P, when you try and access its members,
BASIC will expand a line like 'P.Int1%=2' into 'PRINTInt1%=2' which is
definitely not what you wanted. Single letter names don't fall into our
book of good programming practices anyway.
The easiest way around this problem
is to use meaningful names that have mixed case. It is possible to turn the
abbreviated names on and off: from the menu choose
Options | Customize
then untick the box in the Program Editor group. The problem with this
is that the option is on by default, so if you give your code to anyone
else, they would have a problem and, because they are not as clever as
you, wouldn't know what had happened.
|
If you read the help files, it's possible to do wonders with
structures, but this introduction will suffice for many a while to come.
Exercises
1) If you wanted to store a pupil's grades for five subjects along with their
first name and surname, can you suggest a structure that would do this?
2) Write a program that declares such a structure and prompts for the
information. Calculate the average grade and print the results.
3) If you had 20 pupils in a class, how would you make an array of the
above structure?
© Peter Nairn 2006