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 ); (* get memory *) list ^ := 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.