Previous Table of Contents Next


The implementation makes heavy use of unconstrained array attributes, as seen in the first function, which scales the vector by multiplying each of its elements by the same Float value. Line 5

   Result: Vector(Right’Range);

creates a temporary result vector Result whose bounds are given by Right’Range. Equivalently you could say

   Result: Vector(Right’First..Right’Last);

Right’First is the value of the first index or subscript of Right, not the first element value. This line, then, “sizes” the result vector to be the same as the input vector. Similarly, the loop in lines 7-9 is also bounded by Right’Range and thus multiplies each vector element by the Float value K.

The operator + (lines 13-25) first sizes a result vector (line 14), choosing Left’s bounds as the ones to use. Because the operation makes mathematical sense only if the two vectors have the same length, I check this in lines 16-17, raising the exception if the lengths disagree.

If the vectors are conformable (the lengths agree), I compute the sum by looping through both vectors. Because the bounds will not, in general, be the same, I loop using Left’s bounds and adjust the index into Right accordingly, selecting

   Left(I) + Right(I - Left’First + Right’First)

The inner product (“*”, lines 27-39) is similar to the sum, but this time I multiply the elements pairwise, accumulating them in a scalar Result.

As this package has shown, unconstrained arrays and their attributes provide a concise way to operate on arrays whose bounds are unknown at compilation time. This gives you great expressive power to write general-purpose mathematical algorithms that can operate on vectors and matrices of any size, without knowing their specific bounds.

10.4.2.7. Generic Packages and Matrices

HB.Vectors shows the power of unconstrained array types, but only in a single dimension. Also, the vector element type is specified as Float, which makes the package insufficiently general. In the next example, I extend HB.Vectors in two ways: I illustrate multidimensional unconstrained array types and I show a generic or template package for vector and matrix operations that allows instantiation for an arbitrary element type. Here is the interface for HB.Matrices_Generic.

    1 generic
    2
    3   type   ElementType is private;
    4   Zero:  ElementType;
    5   Unity: ElementType;
    6
    7   with function
    8      “+”(Left, Right: ElementType) return ElementType;
    9   with function
   10      “*”(Left, Right: ElementType) return ElementType;
   11
   12 package HB.Matrices_Generic is
   13
   14   type Vector is array(Integer range <>) of ElementType;
   15   type Matrix is
   16     array (Integer range <>, Integer range <>)
   17     of ElementType;
   18
   19   Bounds_Error: exception;
   20
   21   function “*”
   22     (K: ElementType; Right: Vector) return Vector;
   23   function “*” (Left, Right: Vector) return ElementType;
   24   function “+” (Left, Right: Vector) return Vector;
   25
   26   function “*”
   27     (K: ElementType; Right: Matrix) return Matrix;
   28   function Transpose (Left: Matrix) return Matrix;
   29   function “+” (Left, Right: Matrix) return Matrix;
   30   function “*” (Left, Right: Matrix) return Matrix;
   31
   32   function “*”
   33     (Left: Vector; Right: Matrix) return Vector;
   34   function “*”
   35     (Left: Matrix; Right: Vector) return Vector;
   36
   37 end HB.Matrices_Generic;

Lines 1-11 give the generic part of a package template. Syntactically, this generic part precedes an otherwise ordinary-looking package interface. Lines 3-10 give the generic formal parameters. Just as the caller of an ordinary procedure or function must supply actual parameters to match that subprogram’s formal parameters, so a client of a generic template must supply actual parameters for each of the generic formals.

Line 3 declares a generic formal type parameter. Syntactically, this looks similar to an ordinary type declaration, but it is not; it declares a type parameter. Ada provides for different categories of generic formal parameters, such as integer types, float types, enumeration types, constrained and unconstrained array types, and so on. Each parameter category has its own bit of syntax for describing the category.

In this case, the parameter is a private type parameter. This does not mean that the actual must be private, but rather that the actual may be any type, including a private one. This excludes from consideration only limited and tagged types (which I have not introduced yet) and thus allows great flexibility in the kinds of types that can serve as matrix elements. I could instantiate with predefined scalar types such as Integer, or Float, or Boolean (there are many applications for boolean matrices) or even programmer-defined private composite types such as Rational.

To understand the remaining formal parameters, first recall from HB.Vectors that the inner product operation first initializes a Sum variable to 0.0 and then, as it loops through the vectors, carries out Float addition and multiplication on the vector elements and Sum. In the generic package, the element type is no longer Float, but an arbitrary type to be supplied by the client. This means that 0.0 no longer is a correct zero value. In fact, each possible element type has its own zero: 0 (Integer), 0/1 (Rational), False (Boolean), and so on. In return for the ability to instantiate a matrix package for any type, the client gains the responsibility to tell us (line 4) what the zero is for that type. Similarly, I ask the client to supply a unity value (line 5) for the type.


Previous Table of Contents Next