Generics

Display Array

Your goal for this exercise is to create a generic procedure called Display_Array that displays the elements of an array. This procedure must first display a header and then the elements of the array. When displaying the elements, it must use one line per element and include the corresponding index of the array. This is the expected format:

<HEADER>
<index #1>: <element #1>
<index #2>: <element #2>
...

For example, for the following code:

procedure Test is
   A : Int_Array (1 .. 2) := (1, 5);
begin
   Display_Int_Array ("Elements of A", A);;
end Test;

The output is:

Elements of A
 1:  1
 2:  5

These are the formal parameter of the procedure:

  • a range type T_Range for the the array;

  • a formal type T_Element for the elements of the array;

    • This type must be declared in such a way, so that it can be mapped to any type in the instantiation — including record types.
  • an array type T_Array using T_Range and T_Element;

  • a function Image that converts a variable of type T_Element to a String.

generic procedure Display_Array (Header : String; A : T_Array);
with Ada.Text_IO; use Ada.Text_IO; procedure Display_Array (Header : String; A : T_Array) is begin null; end Display_Array;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Display_Array; procedure Main is type Test_Case_Index is (Int_Array_Chk, Point_Array_Chk); procedure Test_Int_Array is type Int_Array is array (Positive range <>) of Integer; procedure Display_Int_Array is new Display_Array (T_Range => Positive, T_Element => Integer, T_Array => Int_Array, Image => Integer'Image); A : constant Int_Array (1 .. 5) := (1, 2, 5, 7, 10); begin Display_Int_Array ("Integers", A); end Test_Int_Array; procedure Test_Point_Array is type Point is record X : Float; Y : Float; end record; type Point_Array is array (Natural range <>) of Point; function Image (P : Point) return String is begin return "(" & Float'Image (P.X) & ", " & Float'Image (P.Y) & ")"; end Image; procedure Display_Point_Array is new Display_Array (T_Range => Natural, T_Element => Point, T_Array => Point_Array, Image => Image); A : constant Point_Array (0 .. 3) := ((1.0, 0.5), (2.0, -0.5), (5.0, 2.0), (-0.5, 2.0)); begin Display_Point_Array ("Points", A); end Test_Point_Array; procedure Check (TC : Test_Case_Index) is begin case TC is when Int_Array_Chk => Test_Int_Array; when Point_Array_Chk => Test_Point_Array; 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;

Average of Array of Float

In this exercise, you'll create a generic function Average that calculates the average of an array containing floating-point values of arbitrary precision. This function must contain the following formal parameters:

  • a range type T_Range for the array;
  • a formal type T_Element that can be mapped to floating-point types of arbitrary precision;
  • an array type T_Array using T_Range and T_Element;
generic function Average (A : T_Array) return T_Element;
function Average (A : T_Array) return T_Element is begin return 0.0; end Average;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Average; procedure Main is type Test_Case_Index is (Float_Array_Chk, Digits_12_Float_Array_Chk); procedure Test_Float_Array is type Float_Array is array (Positive range <>) of Float; function Average_Float is new Average (T_Range => Positive, T_Element => Float, T_Array => Float_Array); A : constant Float_Array (1 .. 5) := (1.0, 3.0, 5.0, 7.5, -12.5); begin Put_Line ("Average: " & Float'Image (Average_Float (A))); end Test_Float_Array; procedure Test_Digits_12_Float_Array is type Custom_Float is digits 12; type Float_Array is array (Integer range <>) of Custom_Float; function Average_Float is new Average (T_Range => Integer, T_Element => Custom_Float, T_Array => Float_Array); A : constant Float_Array (-1 .. 3) := (-1.0, 3.0, 5.0, 7.5, 12.5); begin Put_Line ("Average: " & Custom_Float'Image (Average_Float (A))); end Test_Digits_12_Float_Array; procedure Check (TC : Test_Case_Index) is begin case TC is when Float_Array_Chk => Test_Float_Array; when Digits_12_Float_Array_Chk => Test_Digits_12_Float_Array; 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;

Average of Array of Decimal Fixed-Point

This exercise is based on the implementation that you created for the previous exercise. Here, your task is to adapt the generic function Average to calculate the average of decimal fixed-point values. You may want to reuse the previous implementation as a starting point.

generic function Average (A : T_Array) return T_Element;
function Average (A : T_Array) return T_Element is begin return 0.0; end Average;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Average; procedure Main is type Test_Case_Index is (Decimal_Array_Chk, Delta_EM4_Digits_16_Float_Array_Chk); procedure Test_Decimal_Array is type Decimal is delta 10.0 ** (-2) digits 12; type Decimal_Array is array (Integer range <>) of Decimal; function Average_Decimal is new Average (T_Range => Integer, T_Element => Decimal, T_Array => Decimal_Array); A : constant Decimal_Array (-2 .. 2) := (-1.0, 3.0, 5.0, 7.5, 12.5); begin Put_Line ("Average: " & Decimal'Image (Average_Decimal (A))); end Test_Decimal_Array; procedure Test_Delta_EM4_Digits_16_Float_Array is type Decimal is delta 10.0 ** (-4) digits 16; type Decimal_Array is array (Positive range <>) of Decimal; function Average_Decimal is new Average (T_Range => Positive, T_Element => Decimal, T_Array => Decimal_Array); A : constant Decimal_Array (2 .. 6) := (2.0, 5.0, 2.0, 8.5, -11.5); begin Put_Line ("Average: " & Decimal'Image (Average_Decimal (A))); end Test_Delta_EM4_Digits_16_Float_Array; procedure Check (TC : Test_Case_Index) is begin case TC is when Decimal_Array_Chk => Test_Decimal_Array; when Delta_EM4_Digits_16_Float_Array_Chk => Test_Delta_EM4_Digits_16_Float_Array; 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;

Average of Array of Any Type

In this exercise, you'll abstract the Average function from the previous exercises a step further. In this case, the function shall be able to calculate the average of any arbitrary type — including arrays containing elements of record types. Since record types can be composed by many components of different types, we need to provide a way to indicate which component (or components) of the record should be used when calculating the average of the array. This problem is solved by specifying a To_Float function as a formal parameter, which converts the arbitrary element of T_Element type to the Float type. In the implementation of the Average function, you'll use the To_Float function and calculate the average using a floating-point variable.

In addition, you'll also work on the implementation of the test procedures Test_Decimal_Array and Test_Item_Array:

  • For the Test_Decimal_Array procedure, you'll work with the decimal fixed-point type Decimal.
  • In the case of Test_Item_Array, you'll work with the record type Item, which contains the Quantity and Price components.

This is what you have to do for both procedures:

  • Create the To_Float function.

    • For the Decimal type, the function is pretty straightforward.

    • For the Item type, you'll actually create two functions to convert to floating-point type:

      • Get_Total, which returns the multiplication of the quantity and the price components of the Item type;
      • Get_Price, which returns just the price.
  • Instantiate the Average function.

    • For the Decimal type, you'll declare the Average_Decimal function.
    • For the Item type, you'll declare the Average_Total and Average_Price functions using, respectively, the Get_Total and Get_Price functions mentioned above.
  • Instantiate the generic standard package Ada.Text_IO.Float_IO as F_IO.

  • Use the Put procedure from Ada.Text_IO.Float_IO.

    • This is the specification of the Put procedure, as described in the appendix A.10.9 of the Ada Reference Manual:

      procedure Put(Item : in Num;
                    Fore : in Field := Default_Fore;
                    Aft  : in Field := Default_Aft;
                    Exp  : in Field := Default_Exp);
      
    • For the test procedures you're working on, this is the expected format when calling Put from Float_IO:

      Function Fore Aft Exp
      Test_Decimal_Array 1 2 0
      Test_Item_Array 3 2 0
generic function Average (A : T_Array) return Float;
function Average (A : T_Array) return Float is begin null; end Average;
procedure Test_Decimal_Array;
with Ada.Text_IO; use Ada.Text_IO; with Average; procedure Test_Decimal_Array is type Decimal is delta 10.0 ** (-2) digits 12; type Decimal_Array is array (Integer range <>) of Decimal; A : constant Decimal_Array (-2 .. 2) := (-1.0, 3.0, 5.0, 7.5, 12.5); begin Put ("Average: "); F_IO.Put (Average_Decimal (A)); New_Line; end Test_Decimal_Array;
procedure Test_Item_Array;
with Ada.Text_IO; use Ada.Text_IO; with Average; procedure Test_Item_Array is type Amount is delta 0.01 digits 12; type Item is record Quantity : Natural; Price : Amount; end record; type Item_Array is array (Positive range <>) of Item; A : constant Item_Array (1 .. 4) := ((Quantity => 5, Price => 10.00), (Quantity => 80, Price => 2.50), (Quantity => 40, Price => 5.00), (Quantity => 20, Price => 12.50)); begin Put ("Average per item & quantity: "); F_IO.Put (Average_Total (A)); New_Line; Put ("Average price: "); F_IO.Put (Average_Price (A)); New_Line; end Test_Item_Array;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Test_Decimal_Array; with Test_Item_Array; procedure Main is type Test_Case_Index is (Decimal_Array_Chk, Item_Array_Chk); procedure Check (TC : Test_Case_Index) is begin case TC is when Decimal_Array_Chk => Test_Decimal_Array; when Item_Array_Chk => Test_Item_Array; 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;

Generic list

In previous labs, you've been implementing lists for a variety of types. The List of Names exercise from the Arrays labs is an example. In this exercise, you'll abstract those list implementations in order to create the generic Gen_List package. This package must have the following subprograms:

  • a procedure Init to initialize the list;

  • a procedure Add to add an item to the list;

    • This procedure must contain a Status output parameter that is set to False when the list was full — i.e. when the procedure couldn't add the item;
  • a procedure Display to display the complete list.

    • This includes the name of the list and its elements — using one line per element. This is the expected format:
    <NAME>
    <element #1>
    <element #2>
    ...
    

These are the formal parameters of the Gen_List package:

  • an arbitrary formal type Item;

  • an unconstrained array type Items of Item element with positive range;

  • the Name parameter containing the name of the list;

    • This must be a formal input object of String type.
    • It's used in the Display procedure.
  • an actual array List_Array to store the list;

    • This must be a formal in out object of Items type.
  • the variable Last to store the index of the last element;

    • This must be a formal in out object of Natural type.
  • a procedure Put for the Item type.

    • This procedure is used in the Display procedure to display individual elements of the list.

Also, you'll work on two test procedures:

  • the Test_Int_List procedure to test a list of elements of Integer type;
  • the Test_String_List procedure to test a list of elements of access to String type.

For both test procedures, you'll have to:

  • add missing type declarations;

  • declare and implement a Put procedure for individual elements of the list;

  • declare instances of the Gen_List package.

    • the Int_List package for the Test_Int_List procedure;
    • the String_List package for the Test_String_List procedure.
generic package Gen_List is procedure Init; procedure Add (I : Item; Status : out Boolean); procedure Display; end Gen_List;
with Ada.Text_IO; use Ada.Text_IO; package body Gen_List is procedure Init is begin null; end Init; procedure Add (I : Item; Status : out Boolean) is begin null; end Add; procedure Display is begin null; end Display; end Gen_List;
procedure Test_Int_List;
with Ada.Text_IO; use Ada.Text_IO; with Gen_List; procedure Test_Int_List is type Integer_Array is array (Positive range <>) of Integer; A : Integer_Array (1 .. 3); L : Natural; Success : Boolean; procedure Display_Add_Success (Success : Boolean) is begin if Success then Put_Line ("Added item successfully!"); else Put_Line ("Couldn't add item!"); end if; end Display_Add_Success; begin Int_List.Init; Int_List.Add (2, Success); Display_Add_Success (Success); Int_List.Add (5, Success); Display_Add_Success (Success); Int_List.Add (7, Success); Display_Add_Success (Success); Int_List.Add (8, Success); Display_Add_Success (Success); Int_List.Display; end Test_Int_List;
procedure Test_String_List;
with Ada.Text_IO; use Ada.Text_IO; with Gen_List; procedure Test_String_List is type String_Array is array (Positive range <>) of String_Access; A : String_Array (1 .. 3); L : Natural; Success : Boolean; procedure Display_Add_Success (Success : Boolean) is begin if Success then Put_Line ("Added item successfully!"); else Put_Line ("Couldn't add item!"); end if; end Display_Add_Success; begin String_List.Init; String_List.Add (new String'("Hello"), Success); Display_Add_Success (Success); String_List.Add (new String'("World"), Success); Display_Add_Success (Success); String_List.Add (new String'("Bye"), Success); Display_Add_Success (Success); String_List.Add (new String'("Wait"), Success); Display_Add_Success (Success); String_List.Display; end Test_String_List;
with Ada.Command_Line; use Ada.Command_Line; with Ada.Text_IO; use Ada.Text_IO; with Test_Int_List; with Test_String_List; procedure Main is type Test_Case_Index is (Int_List_Chk, String_List_Chk); procedure Check (TC : Test_Case_Index) is begin case TC is when Int_List_Chk => Test_Int_List; when String_List_Chk => Test_String_List; 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;