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 Lefts 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 vectors 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 Adas 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 dont have to be static; they can be computed as the program executes, so slicing turns out to give considerable expressiveness in array-handling algorithms.
Adas 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 brevitys 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(RightRange); 6 begin 7 for I in RightRange 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(LeftRange); 15 begin 16 if LeftLength /= RightLength then 17 raise Bounds_Error; 18 else 19 for I in Leftrange loop 20 Result(I) := 21 Left(I) + Right(I - LeftFirst + RightFirst); 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 LeftLength /= RightLength then 31 raise Bounds_Error; 32 else 33 for I in LeftRange loop 34 Sum := Sum + 35 Left(I) * Right(I - LeftFirst + RightFirst); 36 end loop; 37 return Sum; 38 end if; 39 end *; 40 41 end HB.Vectors;
Previous | Table of Contents | Next |