More About Types

Aggregate Initialization

Goal: initialize records and arrays using aggregates.

Steps:

  1. Implement the Aggregates package.

    1. Create the record type Rec.

    2. Create the array type Int_Arr.

    3. Implement the Init procedure that outputs a record of Rec type.

    4. Implement the Init_Some procedure.

    5. Implement the Init procedure that outputs an array of Int_Arr type.

Requirements:

  1. Record type Rec has four components of Integer type. These are the components with the corresponding default values:

    • W = 10

    • X = 11

    • Y = 12

    • Z = 13

  2. Array type Int_Arr has 20 elements of Integer type (with indices ranging from 1 to 20).

  3. The first Init procedure outputs a record of Rec type where:

    1. X is initialized with 100,

    2. Y is initialized with 200, and

    3. the remaining elements use their default values.

  4. Procedure Init_Some outputs an array of Int_Arr type where:

    1. the first five elements are initialized with the value 99, and

    2. the remaining elements are initialized with the value 100.

  5. The second Init procedure outputs an array of Int_Arr type where:

    1. all elements are initialized with the value 5.

package Aggregates is -- type Rec is ...; -- type Int_Arr is ...; procedure Init; -- procedure Init_Some ...; -- procedure Init ...; end Aggregates;
package body Aggregates is procedure Init is null; end Aggregates;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Aggregates; use Aggregates; procedure Main is -- Remark: the following line is not relevant. F : array (1 .. 10) of Float := (others => 42.42) with Unreferenced; type Test_Case_Index is (Default_Rec_Chk, Init_Rec_Chk, Init_Some_Arr_Chk, Init_Arr_Chk); procedure Check (TC : Test_Case_Index) is A : Int_Arr; R : Rec; DR : constant Rec := (others => <>); begin case TC is when Default_Rec_Chk => R := DR; Put_Line ("Record Default:"); Put_Line ("W => " & Integer'Image (R.W)); Put_Line ("X => " & Integer'Image (R.X)); Put_Line ("Y => " & Integer'Image (R.Y)); Put_Line ("Z => " & Integer'Image (R.Z)); when Init_Rec_Chk => Init (R); Put_Line ("Record Init:"); Put_Line ("W => " & Integer'Image (R.W)); Put_Line ("X => " & Integer'Image (R.X)); Put_Line ("Y => " & Integer'Image (R.Y)); Put_Line ("Z => " & Integer'Image (R.Z)); when Init_Some_Arr_Chk => Init_Some (A); Put_Line ("Array Init_Some:"); for I in A'Range loop Put_Line (Integer'Image (I) & " " & Integer'Image (A (I))); end loop; when Init_Arr_Chk => Init (A); Put_Line ("Array Init:"); for I in A'Range loop Put_Line (Integer'Image (I) & " " & Integer'Image (A (I))); end loop; end case; end Check; begin if Argument_Count < 1 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 1 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1))); end Main;

Versioning

Goal: implement a simple package for source-code versioning.

Steps:

  1. Implement the Versioning package.

    1. Declare the record type Version.

    2. Implement the Convert function that returns a string.

    3. Implement the Convert function that returns a floating-point number.

Requirements:

  1. Record type Version has the following components of Natural type:

    1. Major,

    2. Minor, and

    3. Maintenance.

  2. The first Convert function returns a string containing the version number.

  3. The second Convert function returns a floating-point value.

    1. For this floating-point value:

      1. the number before the decimal point must correspond to the major number, and

      2. the number after the decimal point must correspond to the minor number.

      3. the maintenance number is ignored.

    2. For example, version "1.3.5" is converted to the floating-point value 1.3.

    3. An obvious limitation of this function is that it can only handle one-digit numbers for the minor component.

      • For example, we cannot convert version "1.10.0" to a reasonable value with the approach described above. The result of the call Convert ((1, 10, 0)) is therefore unspecified.

      • For the scope of this exercise, only version numbers with one-digit components are checked.

Remarks:

  1. We use overloading for the Convert functions.

  2. For the function Convert that returns a string, you can make use of the Image_Trim function, as indicated in the source-code below — see package body of Versioning.

package Versioning is -- type Version is record... -- function Convert ... -- function Convert end Versioning;
with Ada.Strings; use Ada.Strings; with Ada.Strings.Fixed; use Ada.Strings.Fixed; package body Versioning is function Image_Trim (N : Natural) return String is S_N : constant String := Trim (Natural'Image (N), Left); begin return S_N; end Image_Trim; -- function Convert ... -- S_Major : constant String := Image_Trim (V.Major); -- S_Minor : constant String := Image_Trim (V.Minor); -- S_Maint : constant String := Image_Trim (V.Maintenance); -- begin -- end Convert; -- function Convert ... -- begin -- end Convert; end Versioning;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Versioning; use Versioning; procedure Main is type Test_Case_Index is (Ver_String_Chk, Ver_Float_Chk); procedure Check (TC : Test_Case_Index) is V : constant Version := (1, 3, 23); begin case TC is when Ver_String_Chk => Put_Line (Convert (V)); when Ver_Float_Chk => Put_Line (Float'Image (Convert (V))); end case; end Check; begin if Argument_Count < 1 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 1 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1))); end Main;

Simple todo list

Goal: implement a simple to-do list system.

Steps:

  1. Implement the Todo_Lists package.

    1. Declare the Todo_Item type.

    2. Declare the Todo_List type.

    3. Implement the Add procedure.

    4. Implement the Display procedure.

Requirements:

  1. Todo_Item type is used to store a to-do item.

    1. It should be implemented as an access type to strings.

  2. Todo_Items type is an array of to-do items.

    1. It should be implemented as an unconstrained array with positive range.

  3. Todo_List type is the container for all to-do items.

    1. This record type must have a discriminant for the maximum number of elements of the list.

    2. In order to store the to-do items, it must contain a component named Items of Todo_Items type.

    3. Don't forget to keep track of the last element added to the list!

      • You should declare a Last component in the record.

  4. Procedure Add adds items (of Todo_Item type) to the list (of Todo_List type).

    1. This requires allocating a string for the access type.

    2. An item can only be added to the list if the list isn't full yet — see next point for details on error handling.

  5. Since the number of items that can be stored on the list is limited, the list might eventually become full in a call to Add.

    1. You must write code in the implementation of the Add procedure that verifies this condition.

    2. If the procedure detects that the list is full, it must display the following message: "ERROR: list is full!".

  6. Procedure Display is used to display all to-do items.

    1. It must display one item per line.

Remarks:

  1. We use access types and unconstrained arrays in the implementation of the Todo_Lists package.

package Todo_Lists is -- Replace by actual type declaration type Todo_Item is null record; -- Replace by actual type declaration type Todo_Items is null record; -- Replace by actual type declaration type Todo_List is null record; procedure Add (Todos : in out Todo_List; Item : String); procedure Display (Todos : Todo_List); end Todo_Lists;
with Ada.Text_IO; use Ada.Text_IO; package body Todo_Lists is procedure Add (Todos : in out Todo_List; Item : String) is begin Put_Line ("ERROR: list is full!"); end Add; procedure Display (Todos : Todo_List) is begin null; end Display; end Todo_Lists;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Todo_Lists; use Todo_Lists; procedure Main is type Test_Case_Index is (Todo_List_Chk); procedure Check (TC : Test_Case_Index) is T : Todo_List (10); begin case TC is when Todo_List_Chk => Add (T, "Buy milk"); Add (T, "Buy tea"); Add (T, "Buy present"); Add (T, "Buy tickets"); Add (T, "Pay electricity bill"); Add (T, "Schedule dentist appointment"); Add (T, "Call sister"); Add (T, "Revise spreasheet"); Add (T, "Edit entry page"); Add (T, "Select new design"); Add (T, "Create upgrade plan"); Display (T); end case; end Check; begin if Argument_Count < 1 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 1 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1))); end Main;

Price list

Goal: implement a list containing prices

Steps:

  1. Implement the Price_Lists package.

    1. Declare the Price_Type type.

    2. Declare the Price_List record.

    3. Implement the Reset procedure.

    4. Implement the Add procedure.

    5. Implement the Get function.

    6. Implement the Display procedure.

Requirements:

  1. Price_Type is a decimal fixed-point data type with a delta of two digits (e.g. 0.01) and twelve digits in total.

  2. Price_List is a record type that contains the price list.

    1. This record type must have a discriminant for the maximum number of elements of the list.

  3. Procedure Reset resets the list.

  4. Procedure Add adds a price to the list.

    1. You should keep track of the last element added to the list.

  5. Function Get retrieves a price from the list using an index.

    1. This function returns a record instance of Price_Result type.

    2. Price_Result is a variant record containing:

      1. the Boolean component Ok, and

      2. the component Price (of Price_Type).

    3. The returned value of Price_Result type is one of the following:

      1. If the index specified in a call to Get contains a valid (initialized) price, then

        • Ok is set to True, and

        • the Price component contains the price for that index.

      2. Otherwise:

        • Ok is set to False, and

        • the Price component is not available.

  6. Procedure Display shows all prices from the list.

    1. The header (first line) must be PRICE LIST.

    2. The remaining lines contain one price per line.

    3. For example:

      • For the following code:

        procedure Test is
           L : Price_List (10);
        begin
           Reset (L);
           Add (L, 1.45);
           Add (L, 2.37);
           Display (L);
        end Test;
        
      • The output is:

        PRICE LIST
         1.45
         2.37
        

Remarks:

  1. To implement the package, you'll use the following features of the Ada language:

    1. decimal fixed-point types;

    2. records with discriminants;

    3. dynamically-sized record types;

    4. variant records.

  2. For record type Price_List, you may use an unconstrained array as a component of the record and use the discriminant in the component declaration.

package Price_Lists is -- Replace by actual type declaration type Price_Type is new Float; -- Replace by actual type declaration type Price_List is null record; -- Replace by actual type declaration type Price_Result is null record; procedure Reset (Prices : in out Price_List); procedure Add (Prices : in out Price_List; Item : Price_Type); function Get (Prices : Price_List; Idx : Positive) return Price_Result; procedure Display (Prices : Price_List); end Price_Lists;
package body Price_Lists is procedure Reset (Prices : in out Price_List) is begin null; end Reset; procedure Add (Prices : in out Price_List; Item : Price_Type) is begin null; end Add; function Get (Prices : Price_List; Idx : Positive) return Price_Result is begin null; end Get; procedure Display (Prices : Price_List) is begin null; end Display; end Price_Lists;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Price_Lists; use Price_Lists; procedure Main is type Test_Case_Index is (Price_Type_Chk, Price_List_Chk, Price_List_Get_Chk); procedure Check (TC : Test_Case_Index) is L : Price_List (10); procedure Local_Init_List is begin Reset (L); Add (L, 1.45); Add (L, 2.37); Add (L, 3.21); Add (L, 4.14); Add (L, 5.22); Add (L, 6.69); Add (L, 7.77); Add (L, 8.14); Add (L, 9.99); Add (L, 10.01); end Local_Init_List; procedure Get_Display (Idx : Positive) is R : constant Price_Result := Get (L, Idx); begin Put_Line ("Attempt Get # " & Positive'Image (Idx)); if R.Ok then Put_Line ("Element # " & Positive'Image (Idx) & " => " & Price_Type'Image (R.Price)); else declare begin Put_Line ("Element # " & Positive'Image (Idx) & " => " & Price_Type'Image (R.Price)); exception when others => Put_Line ("Element not available (as expected)"); end; end if; end Get_Display; begin case TC is when Price_Type_Chk => Put_Line ("The delta value of Price_Type is " & Price_Type'Image (Price_Type'Delta) & ";"); Put_Line ("The minimum value of Price_Type is " & Price_Type'Image (Price_Type'First) & ";"); Put_Line ("The maximum value of Price_Type is " & Price_Type'Image (Price_Type'Last) & ";"); when Price_List_Chk => Local_Init_List; Display (L); when Price_List_Get_Chk => Local_Init_List; Get_Display (5); Get_Display (40); end case; end Check; begin if Argument_Count < 1 then Put_Line ("ERROR: missing arguments! Exiting..."); return; elsif Argument_Count > 1 then Put_Line ("Ignoring additional arguments..."); end if; Check (Test_Case_Index'Value (Argument (1))); end Main;