Dynamic memory is useful when working with records initially stored in a file because the number of records in the file is not known by the program ahead of time. Therefore, it may be worthwhile to allocate the memory for the items in the file as they are read, instead of declaring ahead of time an array of records large enough to hold all the data. The example below is a simple illustration of this idea. As in the sorting example of a previous section, the dynamic data is tracked by a collection of pointers to the data items held in an array and each item is sorted to its correct place in the list by manipulating the pointers in the array, not by reassigning any of the data elements themselves. As in that example, this is more efficient than keeping an array of the entire records, because memory reshuffling is limited to pointers, which take far fewer storage locations and therefore less time to swap or otherwise reassign.
Here, a small record consisting of people's names, initial, and age is kept in a file on the disk. For simplicity, it is supposed that the maximum number of these records is 101. The purpose this simple module serves is to read the items into memory, simultaneously sorting their pointers by the name in the record, and then printing out the entire list. The module also illustrates some elementary interactions between files and dynamic records, though this is by far the last word on the subject.
First a very simple module was written to create a data file. It has no dynamic data or pointers of any kind.
MODULE MakeRecords;
(* very crude data entry
to produce a file for the program GetPrintRecords
by R. Sutcliffe -- modified 1995 05 12 *)
FROM STextIO IMPORT
WriteString, ReadString, ReadChar, WriteLn, SkipLine;
FROM SWholeIO IMPORT
ReadCard, WriteCard;
FROM RndFile IMPORT
ChanId, OpenClean, OpenResults, Close, raw;
FROM RawIO IMPORT
Write;
FROM SIOResult IMPORT
ReadResult, ReadResults;
CONST
max = 100;
TYPE
ShortAry = ARRAY [0..15] OF CHAR;
RecordData =
RECORD
last, first: ShortAry;
initial: CHAR;
age: CARDINAL;
END;
VAR
person: RecordData;
numActive : CARDINAL;
outfile: ChanId;
res : OpenResults;
BEGIN (* main program *)
OpenClean (outfile, "data", raw, res);
IF res = opened
THEN
numActive := 0;
LOOP
WriteString ("reading record number");
WriteCard (numActive +1, 5);
WriteLn;
WriteString ("last name?");
ReadString (person.last);
IF ReadResult () # allRight
THEN
EXIT
END; (* if *)
SkipLine; WriteLn;
WriteString ("first name?");
ReadString (person.first);
IF ReadResult () # allRight
THEN
EXIT
END; (* if *)
SkipLine; WriteLn;
WriteString ("initial?");
ReadChar (person.initial);
IF ReadResult () # allRight
THEN
EXIT
END; (* if *)
SkipLine; WriteLn;
WriteString ("age?");
ReadCard (person.age);;
IF ReadResult () # allRight
THEN
EXIT
END; (* if *)
SkipLine;
Write (outfile, person);
INC (numActive);
END; (* loop *)
Close (outfile);
ELSE
WriteString ("could not open file");
END (* initial if res *)
END MakeRecords.
Next, the main module was produced to read, sort and print this data. To keep things simple so that the focus can be on the dynamic nature of the memory allocation error checking is primitive or non-existent. The sorting routine should be considered carefully, however. This particular one is called an insert sort, and will be discussed in detail in chapter 13, but something like it will be used in other places in the chapter. It works by examining each item in a sorted list and then inserting the new one in its appropriate place.
MODULE GetPrintRecords;
(* By R. Sutcliffe
to illustrate dynamic records
modified 1995 05 12 *)
FROM STextIO IMPORT
WriteChar, WriteString, WriteLn;
FROM SWholeIO IMPORT
WriteCard;
FROM RndFile IMPORT
ChanId, OpenOld, OpenResults, Close, raw;
FROM RawIO IMPORT
Read;
FROM IOResult IMPORT
ReadResult, ReadResults;
FROM Storage IMPORT
ALLOCATE;
FROM Strings IMPORT
Compare, CompareResults;
CONST
max = 100; (* maximum number of records *)
TYPE
ShortAry = ARRAY [0..15] OF CHAR;
RecordData =
RECORD
last, first: ShortAry;
initial: CHAR;
age: CARDINAL;
END;
DataPoint = POINTER TO RecordData;
ListType = ARRAY [1..max] OF DataPoint; (* pointers to records *)
VAR
person: RecordData; (* one item for I/O *)
list: ListType;
count, numActive : CARDINAL;
infile: ChanId;
res : OpenResults;
dataArrayFull : BOOLEAN;
PROCEDURE InitPointers; (* Set all pointers in the list to NIL *)
VAR
count: CARDINAL;
BEGIN
FOR count := 1 TO max
DO
list [count] := NIL
END
END InitPointers;
PROCEDURE SortOneIn (name: RecordData);
VAR
moveCount, count: CARDINAL;
BEGIN
IF numActive = 0 (* perhaps this is the first one *)
THEN
INC (numActive); (* get it all started *)
NEW (list [1]); (* get memory *)
list [1]^ := name; (* and occupy it *)
ELSIF numActive = max THEN (* no room *)
dataArrayFull := TRUE;
RETURN;
ELSE (* add this one to the array *)
count := 0;
REPEAT
INC (count);
UNTIL (count > numActive)
OR NOT (Compare (name.last, list [count]^.last) = greater);
(* Now move old ones forward in list to insert *)
FOR moveCount := numActive + 1 TO count + 1 BY - 1
DO
list [moveCount] := list [moveCount - 1]
END;
NEW (list [count]); (* new value at this spot *)
list [count]^ := name; (* occupy the memory *)
INC (numActive)
END;
END SortOneIn;
BEGIN (* main program *)
dataArrayFull := FALSE;
InitPointers;
OpenOld (infile, "data", raw, res);
IF res = opened
THEN
numActive := 0;
LOOP
Read (infile, person);
IF ReadResult (infile) # allRight
THEN
EXIT
END; (* if *)
SortOneIn (person);
IF dataArrayFull
THEN
WriteString ("Could not read entire file");
WriteLn;
EXIT;
END; (* if *)
END; (* loop *)
Close (infile);
(* now ready to print it out *)
FOR count := 1 TO numActive
DO
WITH list [count]^
DO
WriteString (first);
WriteString (' ');
WriteChar (initial);
WriteString ('. ');
WriteString (last);
WriteString (' is ');
WriteCard (age, 3);
WriteString (' years old.');
WriteLn;
END (* with *)
END; (* for *)
END (* if res *)
END GetPrintRecords.
Examples of this general nature will be revisited and improved on substantially from time to time in later sections, but for now, the reader will be invited to incorporate at least some cosmetic improvements in the exercises. One set of output from this latter module, after entering some data for a file with the first one, was:
jim t. babchuk is 56 years old. ernest y. borgnine is 89 years old. catherine m. davis is 15 years old. dawn t. moore is 47 years old. garth r. moorehead is 34 years old. tom r. rowan is 26 years old. william r. shakespeare is 390 years old. joel r. sutcliffe is 15 years old. nathan p. sutcliffe is 15 years old.