Previous | Next | Table of Contents | Index | Program List | Copyright

5.8 System Structures: A Useful Functions Package and the Overloading Principle

In Section 4.8 we showed how to write a simple package, Min_Max, containing functions to find the minimum and maximum of two integer values. Let us rework that package to include two more useful mathematical functions, the sum of integers from 1 to N and the product of integers from 1 to N. The latter function is called factorial.

First we shall rewrite the package specification to name the package Useful_Functions and include specifications for the two new functions. Note in the specification that the sum and factorial functions require parameters of type Positive and return Positive results. Program 5.18 shows the package specification.

Program 5.18
Specification for Package Useful_Functions

PACKAGE Useful_Functions IS
------------------------------------------------------------------------
--| Specifications of functions provided by Useful_Functions package
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  FUNCTION Minimum (Value1: Integer;  Value2: Integer) RETURN Integer;
  FUNCTION Minimum (Value1: Float;    Value2: Float)   RETURN Float;
  -- Pre:  Value1 and Value2 have been assigned values
  -- Post: Returns the smaller of the two input values

  FUNCTION Maximum (Value1: Integer;  Value2: Integer) RETURN Integer;
  FUNCTION Maximum (Value1: Float;    Value2: Float)   RETURN Float;
  -- Pre:  Value1 and Value2 have been assigned values
  -- Post: Returns the larger of the two input values

  FUNCTION Sum (N: Positive) RETURN Positive;
  -- Pre:  N has been assigned a value
  -- Post: Returns the sum of integers from 1 to N

  FUNCTION Factorial (N: Positive) RETURN Positive;
  -- Pre:  N has been assigned a value
  -- Post: Returns the product of integers from 1 to N

END Useful_Functions;

The Overloading Principle

There is something else noteworthy about the specification in Program 5.18. Function specifications appear for two functions called Minimum and two functions called Maximum. Looking at the two Minimum functions,
    FUNCTION Minimum (Value1, Value2: Integer) RETURN Integer;
    FUNCTION Minimum (Value1, Value2: Float)   RETURN Float;
we see that they have the same names but different parameter profiles; that is, their input parameters and return types are different. This is an example of overloading, which in Ada allows two or more different operations to have the same name, as long as there is enough difference in their parameter profiles so that the compiler can distinguish them.

The advantage of overloading is that operations with similar behavior or functionality can be given similar names. This makes programs easier to write and to read because the programmer is not forced to invent names like MinimumInteger and MinimumFloat merely because the language requires all subprograms to have distinct names.

Actually, you have been using overloaded operations without realizing it. One example relates to the input/output libraries. Many programs in this book have used procedure calls of the form

    Ada.Integer_Text_IO.Get (Item => CurrentValue);
from the input/output package for integer tokens. On the other hand, in Program 5.6, a procedure call appears
    Ada.Integer_Text_IO.Get (File => TestScores, Item => CurrentValue);
which has the same name, Get, but a different parameter profile: The second procedure call includes a file variable; the first does not. There is no "magic" about this, or anything unique to the I/O libraries--it is just an application of the overloading principle.

When the compiler reaches a statement like one of the two above, it can select the appropriate procedure to include in the executable program by examining the parameter profile. If the profile matches one of the procedures made available by context clauses, all is well. If there is no match, a compilation error results. It could be that there are two matches; this case also results in a compilation error.

Another example comes from the arithmetic operations we have been doing. An assignment statement such as

    Result := Result + Count;
uses a different + depending on whether its operands are Integer or Float. Indeed, the machine instructions generated by the compiler are quite different for the two numeric types. We could write specifications for the integer and float versions of + that look just like function specifications:
    FUNCTION "+" (Left: Integer; Right: Integer) RETURN Integer;
    FUNCTION "+" (Left: Float;   Right: Float  ) RETURN Float;

Mathematically, an arithmetic operation is just a special kind of function; writing an operator specification this way just reflects that mathematical fact. There is no problem in naming both of the operations + (the quotes are required in this form for syntactic reasons): They have different parameter profiles, so the compiler can distinguish between them.

Specifications of all the predefined types and operators in Ada appear in the Language Reference Manual in a section called PACKAGE Standard; a version of this very useful description appears in Appendix C. PACKAGE Standard is automatically available to all Ada programs; no context clause is necessary. When the compiler reaches a statement like

    Result := Result + Count;
it examines the types of Result and Count to discover whether a matching + is available. If Result is integer and Count is float, for example, there is no matching + in PACKAGE Standard, so a compilation error arises.

PROGRAM STYLE
Using Overloading Wisely

Used carefully, overloading can be a very helpful concept in writing Ada programs, because it allows operations to be given meaningful names, and all operations with similar functionality can be given the same name. Clearly, overloading can be abused by using it too much or by using it to name functions and procedures that do not have similar behavior. This would mislead and confuse the reader of a program and so should be avoided.

Writing the Body of Useful_Functions

The next step is to provide the package body of Useful_Functions, which consists of function bodies for the six functions. The body of Sum is adapted from Program 5.3; the body of Factorial can be readily adapted from the body of Sum. (note, however, that Result is initialized to 1, not 0). The complete package body appears in Program 5.19.

Program 5.19
Body of Useful_Functions

PACKAGE BODY Useful_Functions IS
------------------------------------------------------------------------
--| Body of Useful_Functions package
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  -- minimum of two Integer values
  FUNCTION Minimum (Value1: Integer; Value2: Integer) RETURN Integer IS
    Result: Integer;
  BEGIN -- Minimum
    IF Value1 < Value2 THEN
       Result := Value1;
    ELSE
       Result := Value2;
    END IF;
    RETURN Result;
  END Minimum;

  -- minimum of two Float values
  FUNCTION Minimum (Value1: Float; Value2: Float) RETURN Float IS
    Result: Float;
  BEGIN -- Minimum
    IF Value1 < Value2 THEN
       Result := Value1;
    ELSE
       Result := Value2;
    END IF;
    RETURN Result;
  END Minimum;

  -- maximum of two Integer values
  FUNCTION Maximum (Value1: Integer; Value2: Integer) RETURN Integer IS
    Result: Integer;
  BEGIN -- Maximum
    IF Value1 > Value2 THEN
       Result := Value1;
    ELSE
       Result := Value2;
    END IF;
    RETURN Result;
  END Maximum;

  -- maximum of two Float values
  FUNCTION Maximum (Value1: Float; Value2: Float) RETURN Float IS
    Result: Float;
  BEGIN -- Maximum
    IF Value1 > Value2 THEN
       Result := Value1;
    ELSE
       Result := Value2;
    END IF;
    RETURN Result;
  END Maximum;

  -- sum of integers from 1 to N
  FUNCTION Sum (N: Positive) RETURN Positive IS
    Result: Natural;
  BEGIN -- Sum
    Result := 0;
    FOR Count IN 1..N LOOP
      Result := Result + Count;
    END LOOP;
    RETURN Result;
  END Sum;

  -- factorial, or product of integers from 1 to N
  FUNCTION Factorial (N: Positive) RETURN Positive IS
    Result: Positive;
  BEGIN -- Factorial
    Result := 1;
    FOR Count IN 1..N LOOP
      Result := Result * Count;
    END LOOP;
    RETURN Result;
  END Factorial;

END Useful_Functions;
Program 5.20 illustrates the overloading principle in action by finding the maximum of two integers and the maximum of two floats. Notice in this program that Useful_Functions.Maximum appears to be called twice. In fact, different functions are being called, as you can see from the different parameter profiles: In the first call integers are supplied; in the second call floats are supplied.

Program 5.20
Illustrating the Overloading Principle

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
WITH Ada.Float_Text_IO;
WITH Useful_Functions;
PROCEDURE Max_Int_Flt IS
------------------------------------------------------------------------
--| Illustrates the overloading principle using the Maximum
--| functions for both integer and float quantities
--| Author: Michael B. Feldman, The George Washington University
--| Last Modified: July 1995
------------------------------------------------------------------------

  Int1 :      Integer; -- inputs
  Int2 :      Integer;
  LargerInt : Integer; -- output

  Flt1 :      Float;   -- inputs
  Flt2 :      Float;
  LargerFlt : Float;   -- output

BEGIN -- Max_Int_Flt

  Ada.Text_IO.Put (Item => "Please enter first integer value  > ");
  Ada.Integer_Text_IO.Get (Item => Int1);
  Ada.Text_IO.Put (Item => "Please enter second integer value > ");
  Ada.Integer_Text_IO.Get (Item => Int2);

  LargerInt := Useful_Functions.Maximum(Value1=>Int1, Value2=>Int2);

  Ada.Text_IO.Put (Item => "The larger integer is ");
  Ada.Integer_Text_IO.Put (Item => LargerInt, Width => 1);
  Ada.Text_IO.New_Line;

  Ada.Text_IO.Put (Item => "Please enter first float value  > ");
  Ada.Float_Text_IO.Get (Item => Flt1);
  Ada.Text_IO.Put (Item => "Please enter second float value > ");
  Ada.Float_Text_IO.Get (Item => Flt2);

  LargerFlt := Useful_Functions.Maximum(Value1=>Flt1, Value2=>Flt2);

  Ada.Text_IO.Put (Item => "The larger float is ");
  Ada.Float_Text_IO.Put 
    (Item => LargerFlt, Fore => 1, Aft => 2, Exp => 0);
  Ada.Text_IO.New_Line;

END Max_Int_Flt;
Sample Run
Please enter first integer value  > -27
Please enter second integer value > 34
The larger integer is 34
Please enter first float value  > 29.77
Please enter second float value > 15.09
The larger float is 29.77
Finally, Program 5.21 gives a program that prompts the user for an integer between 1 and 10, then displays a table of the sum and factorial of each of the integers from 1 to the number entered.

Program 5.21
A Program that Uses Useful_Functions

WITH Ada.Text_IO;
WITH Ada.Integer_Text_IO;
WITH Useful_Functions;
PROCEDURE Sum_and_Factorial IS
------------------------------------------------------------------------
--| Prompts the user for an integer N from 1 to 10 
--| and displays the sum and factorial of all integers from
--| 1 to N. Sum and Factorial are gotten from Useful_Functions.  .
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                     
------------------------------------------------------------------------

  SUBTYPE OneToTen IS Positive RANGE 1..10;

  MaxNum:       OneToTen; -- input - a value from 1-10  
  SumToCount:   Positive; -- output - sum of integers from 1-Count
  ProdToCount:  Positive; -- output - product of integers from 1-Count
 
BEGIN -- Sum_and_Factorial   

  Ada.Text_IO.Put (Item => "Please enter an integer from 1 to 10 > ");
  Ada.Integer_Text_IO.Get (Item => MaxNum);
  Ada.Text_IO.New_Line;

  Ada.Text_IO.Put(Item => "  N    Sum    Factorial");
  Ada.Text_IO.New_Line;
  Ada.Text_IO.Put(Item => "  ----------------------");
  Ada.Text_IO.New_Line;

  FOR Count IN 1..MaxNum LOOP
    SumToCount   := Useful_Functions.Sum (N => Count);
    ProdToCount  := Useful_Functions.Factorial (N => Count);

    Ada.Integer_Text_IO.Put (Item => Count, Width => 3);
    Ada.Integer_Text_IO.Put (Item => SumToCount, Width => 7);
    Ada.Integer_Text_IO.Put (Item => ProdToCount, Width => 9);
    Ada.Text_IO.New_Line;
  END LOOP;

END Sum_and_Factorial;
Sample Run
Please enter an integer from 1 to 10 > 9

  N    Sum    Factorial
  ----------------------------
  1      1        1
  2      3        2
  3      6        6
  4     10       24
  5     15      120
  6     21      720
  7     28     5040
  8     36    40320
  9     45   362880

PROGRAM STYLE
Displaying a Table

Program 5.21 displays a table of output values. The table heading is displayed, before the loop is reached, by the statements
Ada.Text_IO.Put (Item => " N    Sum    Factorial"); 
Ada.Text_IO.New_Line; 
Ada.Text_IO.Put (Item => " ---------------------"); 
Ada.Text_IO.New_Line; 

The spaces in the first string are used to align the column headings over their respective table values. We have left enough spaces to center the column titles of the respective values. The second string is used to "draw a line" between the column titles and the values. Within the FOR loop, the four statements

Ada.Integer_Text_IO.Put (Item => Count, Width => 3);
Ada.Integer_Text_IO.Put (Item => SumToCount, Width => 7);
Ada.Integer_Text_IO.Put (Item => ProdToCount, Width => 9);
Ada.Text_IO.New_Line;
display three output values on each line of the table, using 19 columns per line.


Previous | Next | Table of Contents | Index | Program List | Copyright

Copyright © 1996 by Addison-Wesley Publishing Company, Inc.