Chapter 6
Using data structures
User-defined TYPE structures and UNIONs are alternatives to using arrays, a method closer to C language, and PASCAL. This might be important if you are making versions of your source code in different languages. Or, if you are a C or PASCAL programmer now trying to do versions in PowerBASIC.
They are a good way to make general purpose programs, with external $include files, to handle specific data formats. They make it easier to use random files. They give you another kind of "reusable software part". They help make PowerBASIC a user-extendible, customizable language.
TYPEs are a way to label groups of data variables, with a single label as a unit, in effect, a table of pointers to data storage locations in memory, with instructions for each item in the table, a list accessed by a single label.
This can save you time by letting you think of processing big sets of data, instead of each piece of data. They are another form of "reusable software parts", like subroutines and functions.
UNIONs are similar to TYPE structures. You can have more than one label for the same data, in effect, more than one pointer to the same storage locations in memory, like an alias.
You might want to use UNIONs, because sometimes a piece of data might be integer and sometimes it might be a string, when a space on a form has more than one kind of answer. Or you might want to refer to a piece of data byte by byte, or as a whole string.
Whereas TYPE differentiates and subdivides, making one record label equal to a list of labels of individual pieces of data, UNIONs say that two things are the same and unifies, making a list of labels all point to the same data storage locations in memory.
The TYPE structure defines a list of pre-defined formatting instructions, so that PowerBASIC will know how to deal with each kind of data in the group.
Instead of thinking in terms of single values, you can think in terms of records, like a whole page, instead of one part of one line on that page.
Because TYPES tell PowerBASIC how to process a whole group of data elements, they are, in a sense, a data handling macro, a custom recipe, a recipe for setting aside memory.
Once defined, you can use them the same way as built-in standard simple data TYPEs, making PowerBASIC a user-extendible, customizable language.
You can place the TYPE or UNION definitions in an $include file, and use them over and over again, just asif they had been added to PowerBASIC.
User-defined TYPE structures can be associated with your random file's buffer.
One advantage to their use is the data labels are more readable months later, or when another person takes over your source code. This is important if you are selling source code toolkits, or you have a lot of projects.
Another advantageous use would be where you might have hard-coded data formatting instructions for a certain kind of data record, and you want to make the formatting details an external $include file, that can be different for different software products. If you sell source code toolkits, or compiled libraries, as add-ons, your users could more easily create programs with different data formats.
Fixed vs Flexible-Length
TYPE structures and UNIONs are fixed-length, complex data TYPEs, constructed at compile time.
PowerBASIC also provides a mechanism for flexible-length strings, to define variable-length data structures, dynamically at runtime. Flex strings are introduced in Chapter *******, and discussed in more detail later in this chapter.
See PowerBASIC Reference Guide, TYPE, and UNION.
See PowerBASIC Reference Guide, Table 2.5, on page 116, for the built-in standard kinds of simple data TYPE.
What are user-defined TYPE structures?
The difference between arrays and user-defined TYPE structures, i.e., user-defined records, is similar to the difference between rows and columns on the monitor screen.
Instead of defining each variable array separately, like a vertical stack of elements all the same TYPE, and then using subscripts, when you define a user-defined TYPE structure, it is like a horizontal layer made up of one each out of all the vertical stacks. The record structure is a mixture of simple data TYPEs.
Imagine that you have a column of integers, a column of text strings, and a column of double-precision numbers. A "user-defined TYPE structure", or custom record, would be like one horizontal row across, with one integer, one textstring, and one double-precision number.
Each record of that TYPE structure has one variable from each vertical array.
Another analogy is instead of putting all the apples in one bushel, and all the oranges in another bushel, and all the nectarines in another bushel, we take one apple, one orange, and one nectarine, and put these in a small basket, and we make 20 baskets just like this.
It is a different way of referring to the same storage locations in memory.
How we define TYPEs and UNIONs
To tell the compiler to create a user-defined TYPE, start with a TYPE command, and a unique name for the new complex data TYPE, and end with END TYPE. The lines in between these two define the field names and their simple (or even complex) data TYPEs and fixed-length sizes.
TYPE NameOfNewComplexDataType TYPE is a reserved word.
...
...
...
...
...
END TYPE END TYPE is a key word.
Example
TYPE FruitBasket
Apple AS STRING *12
Orange AS STRING *12
Nectarine AS STRING *12
END TYPE
DIM ChristmasPresent(1:20) AS FruitBasket
A real recipe, as a further analogy:
TYPE Oatmeal
RolledOats AS STRING *12
Water AS STRING *12
Raisins AS STRING *12
BrownSugar AS STRING *12
Milk AS STRING *12
END TYPE
DIM Breakfast AS Oatmeal
Example
If you have six pieces of data for each person, and ten people on your donors list, you could refer to them in two different ways:
Two ways to dimension them
The array method
dim PersonsName$(10),Address$(10),CityStateZip$(10)
dim Phone$(10),Salary(10),Donated(10)
The user-defined TYPE method
TYPE DonorEntry
PersonsName AS STRING * 24
Address AS STRING * 24
CityStateZip AS STRING * 24
Phone AS STRING * 12
Salary AS LONG
Donated AS LONG
END TYPE
DIM DonorData(1:10) AS DonorEntry
Remarks
The TYPEs of data can be only fixed-length, not variable-length.
Two ways to refer to individual fields
The array method
PersonsName$(1)="John Doe"
Donated(1)=200.00
The user-defined TYPE method
DonorData.PersonsName(1)="John Doe"
DonorData.Donated(1)=200.00
----------------------------------------------------------------------------
The array method
TotalDonated=0
for i=1 to imaxx%
print "PersonsName = ";PersonsName$(i),"Donated = ";Donated(i)
TotalDonated=TotalDonated+Donated(i)
next i
print"Total Donated is ";TotalDonated
The user-defined TYPE method
TotalDonated=0
for i=1 to imaxx%
print "PersonsName = ";DonorData.PersonsName(i);" ";
print "Donated = ";DonorData.Donated(i)
TotalDonated=TotalDonated+DonorData.Donated(i)
next i
print"Total Donated is ";TotalDonated
Remarks
The variable labels are longer, but they will save time in program maintenance, in the long run.
-------------------------------------------------------------------------
Another viewpoint is to take a multidimensional array, with two or more subscripts, and show it done two ways.
DIM Expenses(5,12)
In the array method, all of these data elements must have the same TYPE.
In the user-defined TYPE structure method, you can have several kinds of data TYPE.
TYPE ExpenseEntry
Description AS STRING * 32
VendorCode AS STRING * 6
Category AS STRING * 2
Date AS STRING * 8
Amount AS LONG
END TYPE
DIM ExpenseData(10,12,3) AS ExpenseEntry
Nesting user-defined TYPEs
The multi-level labeling of nested user-defined TYPE structures is parallel to subdirectory pathnames. Instead of a backslash, use a period to separate the different levels of label.
Instead of a "subdirectory" of disk blocks, you have a "subdirectory" of data cell locations, showing the hierarchy of how the pieces of data are related. It is a "data trail", or record of the path thru the levels of data.
Instead of storing the students' names as two separate fields,
StudentLastName$(i), StudentFirstName$(i)
we could instead define a TYPE structure called "NameRec".
TYPE NameRec
Last AS STRING * 20
First AS STRING * 15
Initial AS STRING * 1
END TYPE
TYPE HomeAddress
StreetName AS STRING * 28
HouseNumber AS STRING * 6
City AS STRING * 24
State AS STRING * 20
Zip AS STRING * 10
END TYPE
TYPE TestRecord
WeeklyTest AS INTEGER
MonthlyTest AS INTEGER
SemesterExam AS INTEGER
END TYPE
TYPE GradesEntry
CourseName AS STRING * 30
Dates AS STRING * 8
Teacher AS STRING * 24
GradeEarned AS TestRecord
END TYPE
TYPE PupilRecord
FullName AS NameRec
IDnum AS STRING * 12
Address AS HomeAddress
Phone AS STRING * 12
Transcript AS GradesEntry
END TYPE
DIM StudentRecord(1:30) AS PupilRecord
StudentRecord.Transcript.GradeEarned.WeeklyTest
---------------------------------------------------------------------------
In programs with this TYPE of labeling, you should probably not use periods in variable names in other ways, to prevent confusion.
You will probably not want to go more than two or three levels. You need to keep your names short, so that when you have nested TYPE structures, your variable names will not become too long.
The practical limit of using these nested TYPE structures is the length of the label used to access a variable, up to 255 characters maximum.
Using arrays of user-defined TYPE structures
Arrays of user-defined TYPE structures are even possible!
Just DIM StudentRecord(1:30) AS PupilRecord
then, when you want to refer to a particular field within that record, it would look like this:
for i=1 to ClassSize
PRINT StudentRecord.FullName.Last(i)
next i
Using user-defined TYPEs with
procedures and functions
User-defined TYPE structures work inside procedures and functions,
just fine.
def FNPrintLastNameList
for i=1 to ClassSize
PRINT StudentRecord.FullName.Last(i)
next i
end def
-----------------------------------------------------------------------------
SUB PrintLastNameList( StudentRecord.FullName.Last(i), ClassSize)
for i=1 to ClassSize
PRINT StudentRecord.FullName.Last(i)
next i
end sub
Use with a random disk file's buffer
Instead of hard-coded data formatting instructions for a certain kind of data record, you might want to make the formatting details an external $include file, that can be different for different software products.
This is especially useful in working with random disk files. See chapter 9, Files, page 219, for more information.
**********************************p 163
Storage requirements
To find out how much memory to allocate for a certain user-defined TYPE structure, use the LEN function with the name of the variable in memory, not the name of the TYPE structure.
Obtaining addresses with VARPTR
The address returned by VARPTR (DataRecord) is the address of the first byte of data in that record.
Example
StudentProfile=VARPTR(StudentRecord)
Using VARPTR with the full "data trail" label, like a subdirectory name,
will return the starting byte of that individual field in the record.
Example
TestScore=VARPTR(StudentRecord.Transcript.GradeEarned.WeeklyTest)
UNIONS
In UNIONs, you can have more than one label for the same data location.
It is like declaring an "alias".
UNIONs are particularly helpful in dealing with data in memory as 16-bit
words, 32-bit words, or 8-bit bytes.