Previous Table of Contents Next


10.4.1.10. Type Composition: Arrays and Records

The next example is a miniature information systems application. A company sells five kind of gadgets, indicated by the colors Red, Blue, Green, Yellow, and Black. In a given time period, a transaction file is created in which a transaction indicates a kind of gadget and a positive (sale) or negative (return) monetary quantity. The program Gadget_Report summarizes the sales, giving, for each of the five gadget types, the number of transactions and the total monetary value. For example, an input transaction file containing

   green 56.00
   blue 25.00
   green 2078.00
   black -2065.00

results in this report:

   Gadget Type  Transactions  Value
   --------------------------------
   RED               0         0.00
   BLUE              1        25.00
   GREEN             2      2134.00
   YELLOW            0         0.00
   BLACK             1     -2065.00

    1 with Ada.Text_IO;
    2 with Ada.Integer_Text_IO;
    3 with Ada.Float_Text_IO;
    4 procedure Gadget_Report is
    5
    6   subtype Money is
    7     Float range -1_000_000_000.00..1_000_000_000.00;
    8
    9   type Gadgets is (Red, Blue, Green, Yellow, Black);
   10   package Gadget_IO is
   11     new Ada.Text_IO.Enumeration_IO (Enum => Gadgets);
   12
   13   type SummaryEntry is record
   14     HowMany: Natural := 0;
   15     Net    : Money := 0.00;
   16   end record;
   17
   18   type Summary is array(Gadgets) of SummaryEntry;
   19
   20   TodaysSummary : Summary;
   21   InputData: Ada.Text_IO.File_Type;
   22   WhichKind: Gadgets;
   23   Amount: Money;
   24
   25 begin
   26
   27   Ada.Text_IO.Open(File => InputData,
   28     Mode => Ada.Text_IO.In_File, Name => “today.dat”);
   29
   30   while not
   31       Ada.Text_IO.End_of_File(File => InputData) loop
   32
   33     Gadget_IO.Get(File => InputData, Item => WhichKind);
   34     Ada.Float_Text_IO.Get
   35       (File => InputData, Item => Amount);
   36     Ada.Text_IO.Skip_line(File => InputData);
   37
   38     TodaysSummary(WhichKind).HowMany :=
   39       TodaysSummary(WhichKind).HowMany + 1;
   40     TodaysSummary(WhichKind).Net :=
   41       TodaysSummary(WhichKind).Net + Amount;
   42
   43   end loop;
   44
   45   Ada.Text_IO.Close (File => InputData);
   46
   47   Ada.Text_IO.Put_Line
   48     (Item => “Gadget Type  Transactions  Value”);
   49   Ada.Text_IO.Put_Line
   50     (Item => “--------------------------------”);
   51
   52   for WhichKind in Gadgets loop
   53     Gadget_IO.Put(Item => WhichKind, Width => 7);
   54     Ada.Integer_Text_IO.Put(
   55       Item => TodaysSummary(WhichKind).HowMany,
   56       Width => 12);
   57     Ada.Float_Text_IO.Put(
   58       Item => TodaysSummary(WhichKind).Net,
   59       Fore => 10, Aft => 2, Exp => 0);
   60     Ada.Text_IO.New_Line;
   61   end loop;
   62
   63 end Gadget_Report;

I represent the monetary type as a subtype of the predefined type Float. Line 3 mentions Ada.Float_Text_IO, which allows me to read and write these quantities; lines 6-7 show the subtype declaration. The underscore characters group the digits by threes; they are just used to improve readability.

I declare Gadgets as an enumeration type (line 9). Lines 10-11 constitute a generic instance of the standard generic package Ada.Text_IO.Enumeration_IO. A generic package is a template for a package; in this case, Enumeration_IO is generic because I must supply the name of an enumeration type on whose values input and output is being done. The instance Gadget_IO is capable of reading and writing exactly the five gadget values.

This generic specialization is especially advantageous in reading input values because the input routine (Gadget_IO.Get) validates that an input token in fact belongs to the set of desired values. If the token does not belong to the desired set, the standard exception Ada.Text_IO.Data_Error is raised at the point of the Get call; the exception can be handled by program code, as you shall see in the next example.

In the reporting program, each transaction is read from the input file and the appropriate summaries—number of transactions and net income per gadget kind—are updated. The transaction can then be discarded. To define a summary entry, lines 13-16 declare a record type. A record type—in this case, SummaryEntry—has fields or components—in this case, HowMany and Net; a record type is heterogeneous because in general, each component has its own type. This type declaration creates no objects; it just gives a rule for creating objects from object declarations.

To hold the gadget summaries, line 18 declares an array type Summary. The components of the array are of type SummaryEntry, which illustrates type composition: a programmer-defined array type each of whose elements is a programmer-defined record type. Types can be composed at will in this fashion.

Now, lines 20-23 declare four objects (variables): an array object TodaysSummary, a file object InputData, and two variables to hold the current transaction.

Instead of reading from standard input, this program reads from an external file whose name as seen by the operating system is today.dat. This external file is associated with the file object InputData by the Open call in lines 27-28. The File and Name parameters are obvious; Mode refers to one of the standard sequential file modes In_File, Out_File, and Append_File (an output mode in which writing begins at the end of an existing file). (Ada’s standard library for direct access files also has an InOut_File mode to open a direct file for both reading and writing).

The main loop of this program (lines 30-43) is straightforward: It reads a transaction from the file and updates the summaries. The expression

   TodaysSummary(WhichKind).HowMany

selects the HowMany field of the WhichKind element of the array object TodaysSummary.


Previous Table of Contents Next