Previous Table of Contents Next


The other cases in “/” are straightforward. Note (line 10) the return statement

   return (Numerator => X, Denominator => Y);

in which the parenthesized expression is an aggregate expression, returning a record to the calling program, with its fields set as indicated.

This statement illustrates that in Ada there is no restriction on the return type of a function; specifically, a function may return a record or even an array, as the abstraction requires. This is a significant advantage over languages that require pointers to data structures to be explicitly passed to, and returned from, functions. Because in Ada composite types are generally passed by reference, pointers are indeed involved, but the compiler, not the programmer, is responsible for them.

Lines 16-24 implement Numer and Denom; lines 26-29 implement the equality test. The statement

   return Numer(R1)*Denom(R2) = Numer(R2)*Denom(R1);

returns True or False, depending on whether the two cross-products are equal. Recalling that 2/6 = 3/9, algebraically, confirms the correctness of the boolean cross-product expression.

Note that it is neither necessary nor legal to overload “/=”; overloading “=” automatically overloads “/=”.

Lines 31-49 give the bodies of the other four comparison operators; lines 51-64 implement the three monadic (unary) arithmetic operators, and lines 66-90 implement the dynadic (binary) arithmetic operators. These bodies are all straightforward.

I complete the rationals system by showing the implementation of HB.Rationals.IO; these four procedure bodies are straightforward. Note only that the Get for standard input is implemented by a simple call (line 19) to the other Get for a named file, using Ada.Text_IO.Standard_Input as the filename.

    1 with Ada.Text_IO, Ada.Integer_Text_IO;
    2 use  Ada.Text_IO, Ada.Integer_Text_IO;
    3 package body HB.Rationals.IO is
    4
    5   procedure Get
    6     (File: in File_Type; Item : out Rational) is
    7     N: Integer;
    8     D: Integer;
    9     Dummy: Character;  -- dummy character to hold the “/”
   10   begin
   11     Get(File => File, Item => N);
   12     Get  (File => File, Item => Dummy);
   13     Get(File => File, Item => D);
   14     Item := N/D;
   15   end Get;
   16
   17   procedure Get (Item : out Rational) is
   18   begin
   19     Get(File => Standard_Input, Item => Item);
   20   end Get;
   21
   22   procedure Put
   23     (File: in File_Type; Item : in Rational) is
   24   begin
   25     Put(File => File, Item => Numer(Item), Width => 1);
   26     Put(File => File, Item => ‘/’);
   27     Put(File => File, Item => Denom(Item), Width => 1);
   28   end Put;
   29
   30   procedure Put (Item : in Rational) is
   31   begin
   32     Put(File => Standard_Output, Item => Item);
   33   end Put;
   34
   35 end HB.Rationals.IO;

10.4.2.6. Unconstrained Array Types and Vectors

The next example gives a basic package for manipulating mathematical vectors.

    1 package HB.Vectors is
    2
    3   type Vector is array(Integer range <>) of Float;
    4
    5   Bounds_Error : exception;
    6
    7   function “*” (K : Float; Right : Vector) return Vector;
    8   -- Pre:  K and Right are defined
    9   -- Post: scales the vector by the scalar
   10   --   Result(i) := K * Right(i)
   11
   12   function “*” (Left, Right : Vector) return Float;
   13   -- Pre:    Left and Right are defined
   14   -- Post:   returns the inner product of Left and Right
   15   -- Raises: Bounds_Error if Left and Right
   16   --         have different numbers of elements
   17
   18   function “+” (Left, Right : Vector) return Vector;
   19   -- Pre:    Left and Right are defined
   20   -- Post:   returns the sum of Left and Right
   21   --         Result(i) := Left(i) + Right(i)
   22   --         Result has Left’s bounds
   23   -- Raises: Bounds_Error if Left and Right
   24   --         have different numbers of elements
   25
   26 end HB.Vectors;

In line 3, a vector is defined as an array of Float values. Recall from the earlier gadgets example that the expression Integer range <> indicates an unconstrained array type. This means that each vector’s bounds are an Integer subrange and that these bounds are set not in the type declaration but in the object declaration. For example,

   V: Vector (1..100);
   W: Vector (25..40);
   Z: Vector (-5..10);

are all legal object declarations. Each object is now said to be constrained; its bounds (and the space allocated for it) are set for its lifetime.

Under Ada’s rules, two such constrained objects are assignment-compatible if they have the same length, that is, the same number of elements; thus the bounds do not have to be equal. Because W and Z have the same length (16 elements),

   W := Z;
   Z := W;

are both valid assignments. Moreover, you can work with slices, that is, subarrays. For example,

   V (5..10) := W (32..37);

is valid because both lengths are 6. The slice bounds don’t have to be static; they can be computed as the program executes, so slicing turns out to give considerable expressiveness in array-handling algorithms.

Ada’s predefined String type is, in fact, an unconstrained array type:

   type String is array (Positive range <>) of Character;

so slicing operations operate as substring operations. The standard library packages Ada.Strings, Ada.Strings.Bounded, and Ada.Strings.Unbounded provide full sets of operations on bounded and unbounded strings of varying length, built on top of the basic support for strings as unconstrained arrays. For brevity’s sake, I do not go into more detail on the string operations.

In HB.Vectors, I provide an exception Bounds_Error and several overloaded operators. The sum (+) of two equal-length vectors is a vector of that same length, in which each element is the sum of the corresponding elements in the operand vectors. The inner product (“*”) of two equal-length vectors with Float elements is a Float value, the accumulated sum of all the pairwise products of the operand vectors (a pairwise product is the product of the first elements of the two vectors, or of the second elements, and so on).

As the postconditions show, this exception is raised if the client violates the vector abstraction, that is, attempts to add or multiply vectors of different lengths. If we have

   V: Vector (1..10);
   W: Vector (11..20);
   X: Vector (6..11);
   Z: Vector (26..35);

then

   W := V + Z;

is valid but

   W := X + Z;

is not and the exception is raised.

This, and the vector arithmetic operations, are shown in the package implementation.

    1 package body HB.Vectors is
    2
    3   function “*”
    4     (K : Float; Right : Vector) return Vector is
    5     Result: Vector(Right’Range);
    6   begin
    7     for I in Right’Range loop
    8       Result(I) := K * Right(I);
    9     end loop;
   10     return Result;
   11   end “*”;
   12
   13   function “+” (Left, Right : Vector) return Vector is
   14     Result : Vector(Left’Range);
   15   begin
   16     if Left’Length /= Right’Length then
   17       raise Bounds_Error;
   18     else
   19       for I in Left’range loop
   20         Result(I) :=
   21           Left(I) + Right(I - Left’First + Right’First);
   22       end loop;
   23       return Result;
   24     end if;
   25   end “+”;
   26
   27   function “*” (Left, Right : Vector) return Float is
   28     Sum : Float := 0.0;
   29   begin
   30     if Left’Length /= Right’Length then
   31       raise Bounds_Error;
   32     else
   33       for I in Left’Range loop
   34         Sum := Sum +
   35           Left(I) * Right(I - Left’First + Right’First);
   36       end loop;
   37       return Sum;
   38     end if;
   39   end “*”;
   40
   41 end HB.Vectors;


Previous Table of Contents Next