Pages

Tuesday, August 28, 2012

AdaTutor - Subprograms and Packages (1)

Procedures and Functions

When we compiled procedures Hello and Add, we made it possible for other units
to with them and call them. We can also compile just a specification,
supplying the body later. For example, we could compile

function English_Upper_Case(S :inString)returnString;

and then compile the calling program. When we later write the body, it must
agree with the specification:

However, is separate may be used only at the outermost level. The example below is legal as it stands, but we may not make procedure B separate unless we also make function A separate, thus bringing function A to the outermost level.

A program that begins separate (...)
must be compiled after the program that says is separate, because a subprogram depends on its "parent."

In Ada 95, Hierarchial Libraries provide an alternative to separate
subprograms. We'll learn about Hierarchial Libraries a little later. Also,
the function To_Upper in the Ada 95 package Ada.Characters.Handling is better
than our function English_Upper_Case, because it handles accented letters. We
presented our function only to give a simple example that will work in both
Ada 83 and Ada 95.

A procedure or function specification gives the name, mode, and type of each
parameter. A function specification also gives the type of the result. The
mode of the parameters can be in, out, or in out. The subprogram may read
from, but not write to, in parameters, and it may write to, but not read from,
out parameters. (In Ada 95, it may read out parameters.) If the mode is
omitted, it's assumed to be in. Thus, these two lines have the same effect:

The parameters of a function must always be of mode in, never out or in out.

Note that when several parameters have the same mode and type, they can be
placed in one list, separated by commas. The lists themselves are separated by
semicolons. The two specifications above are equivalent to:

Question

No, in number 1 the items should be separated by commas, not semicolons. In a
subprogram specification, the items in a list are separated by commas, and the
lists are separated by semicolons. Here a "list" means a collection of
parameters sharing the same mode and type (in Integer).

You're right! In a subprogram specification, the items of a list are separated
by commas, and the lists are separated by semicolons. In a list, the mode may
be omitted; it's assumed to be in.

In number 1, the items should be separated by commas. Number 2 has a return
clause in a procedure specification, and number 3 has a mode other than in in a
function specification.

Which one of the above is legal?

Recall that type conversion from Float to Integer in Ada rounds to the nearest
integer. (The Ada 83 standard doesn't specify which way rounding occurs when
the fractional part is exactly 0.5; the Ada 95 standard specifies rounding away
from zero in such cases.) This function Floor computes the largest integer
less than or equal to a given Float, always rounding toward negative infinity:

In Ada 95, the attributes 'Floor, 'Ceiling, and 'Truncation, available for any
floating point type, are better than the above functions. If X is of type
Float, then Float'Floor(X) is the largest integer <= X, Float'Ceiling(X) is the
smallest integer >= X, and Float'Truncation always truncates toward zero.

Default Parameters

The in parameters of a subprogram specification may be given default values.
For example, here's a simplified version of the specification for the procedure
Put in Ada.Integer_Text_IO (the actual specification can be found in
Annex A.10.1
of the Ada 95 RM):

This means that, in calls to Put, the Width and Base parameters are optional.
If Width is omitted, it's assumed to be 6, and if Base is omitted, it's assumed
to be 10. If either of these parameters is given in the call, the default
value is overridden. (The default value for Width is shown here as 6.
Actually, it depends on the implementation of Ada. Of course, the default
value for Base is always 10.)

Default parameters let us make our Ada subprograms both flexible and easy to
use. In other languages, we'd often have to choose between these two
qualities. For example, suppose J is an integer. Here are some calls to Put:

The first parameter in each call could have been given as Item => J, but
everyone remembers that the first parameter of Put is the item, so named
notation seems unnecessary for this parameter. However, Width and Base are
used less frequently. We used named notation for these parameters so the
reader of our code wouldn't have to remember which parameter comes second and
which comes third. Note that if we omit the second parameter and specify the
third, we must use named notation for the third parameter; we're not allowed to
write Put(J, ,16); as in some languages.

If we were writing Put in another language, we'd have to choose either making
the user specify the width and the base in every call (giving flexibility), or
writing Put with only one parameter (giving ease of use). In Ada, default
parameters give us both flexibility and ease of use!

Ada.Text_IO.New_Line has one parameter, Spacing, defaulted to 1. Thus, we can
call New_Line; to get one CR-LF, or, for example, New_Line(3); to get three.

then USA.Year is set to 1776 when the line declaring USA is elaborated. Every
time an object of type Date is declared, its Year field is set to 1776.
However, there's a difference between default values in records and default
parameters in subprograms. We can't write USA := (4, Jul); all fields of a
record must be specified in an aggregate.