![]() |
![]() | |||||
|
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]()
|
Flow Control & Subprograms
Flow Control: Branching & LoopingAn important part of any programming language is the ability to modify the normal top-to-bottom order of statement execution. In Ada (just as in other procedural languages), this is accomplished with decision structures (e.g., conditional statements) and loop structures. These structures provide the programmer with the ability to have the computer follow diverse paths through the code and make the computer a truly useful tool.The if StatementAda if statements determine if some Boolean condition is true or false, and then execute some sequence of statements depending on that determination. The following example program illustrates the basic syntax for the Ada if..then..end if statement, also known as the "guarded if" form.
with Ada.Text_IO;
use Ada.Text_IO;
procedure IfTest is
Answer : Character;
begin
Put("Is it morning (m) or afternoon (a)?");
Get(Answer);
if Answer = 'm' then
Put_Line("Good morning!");
end if;
end IfTest;
A slight modification of the above program illustrates the use of an
"else clause" as part of the if statement.
The following example program shows the basic syntax for the Ada
if..then..else..end if statement.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greetings is
Answer : Character;
begin
Put ("Is it morning (m) or afternoon (a)? ");
Get (Answer);
if Answer = 'm' then
Put_Line ("Good morning!");
else
Put_Line ("Good afternoon!");
end if;
end Greetings;
In Ada, as with other algorithmic languages, if the Boolean "condition" evaluates to
true the then part is executed. In this example, the Boolean condition is
Answer = 'm'. If the user has typed a lowercase 'm' the condition is true,
any other response makes the condition evaluate to false.
When there is a need to select among more than two alternatives, the if..then..elsif..then..end if version of the if statement can be used. As above, if the "condition" is true the then part is executed. Otherwise, the elsif clauses (if any) are checked in first-to-last order, again looking for a true condition. Finally, if none of the conditions are true, the else clause is executed (if there is an else clause). The example below is an improvement on the previous program. It uses the if statement to account for both upper and lowercase responses from the user. It also illustrates the if..then..elsif..then..end if form of the if statement.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greetings is
Answer : Character;
begin
Put ("Is it morning (m) or afternoon (a)? ");
Get (Answer);
if Answer = 'M' then
Answer := 'm';
elsif Answer = 'A' then
Answer := 'a';
end if;
if Answer = 'm' then
Put_Line ("Good morning!");
elsif Answer = 'a' then
Put_Line ("Good afternoon!");
else
Put_Line ("Please type 'm' or 'a'!");
end if;
end Greetings;
The following version of the Greetings program uses the Boolean
connective or to account for the case of the user's response.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greetings is
Answer : Character;
begin
Put ("Is it morning (m) or afternoon (a)? ");
Get (Answer);
if Answer = 'm' or Answer = 'M' then
Put_Line ("Good morning!");
elsif Answer = 'a' or Answer = 'A' then
Put_Line ("Good afternoon!");
else
Put_Line ("Please type 'm' or 'a'!");
end if;
end Greetings;
This program could handle incorrect user responses (e.g., something other
than an a or m) better then it now does. After we have
discussed the looping statements available in Ada, we will revise this
program again.
Nested if statementsAda if statements can be nested to any level. The program below illustrates three levels of nesting.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure IfDemo is
Index, New_Index : Integer := 1;
begin
<<Again>> null;
if Index <= 11 then
Put("Index is");
Put(Index, 3);
if Index < 10 then
if Index > 8 then
Put(" and is less than 10 ");
Put(" and greater than 8.");
else
Put(" and is less than or equal to 8.");
end if;
else
Put(" and is 10 or greater.");
for New_Index in 222..224 loop
Put(" stutter");
end loop;
end if;
New_Line;
Index := Index + 1;
Goto Again;
end if;
end IfDemo;
Whenever you find yourself nesting if statements to multiple levels,
you might consider using an alternative structure (e.g., the case
statement).
The previous program also illustrates the use of the Ada goto statement. It is used in the above example to avoid using the looping statements introduced later in this document ... and not because it is the recommended approach. case statementSometimes you want to execute one of many different sequences of statements, based on the value of a single expression. As shown above, you can do this by using a set of elsif clauses, but most languages (including Ada) provide a simpler structure.Ada's mechanism is the case statement. A case statement computes the value of an expression, and then compares this value to lists of values to determine what to execute next. The program below provides an example of the case statement:
with Ada.Text_IO; use Ada.Text_IO;
procedure Greetings is
Answer : Character;
begin
Put ("Is it morning (m) or afternoon (a)? ");
Get (Answer);
case Answer is
when 'M' | 'm' =>
Put_Line ("Good morning!");
when 'A' | 'a' =>
Put_Line ("Good afternoon!");
when others =>
Put_Line ("Please type 'm' or 'a'!");
end case;
end Greetings;
Note that after each when is a list of one or more possible values,
separated by the | character. The when others clause matches
anything that has not matched anything else. A case statement chooses
one and only one alternative; the alternatives must be exhaustive (cover all
possible cases) and exclusive (you cannot have two when 5 =>
clauses). An Ada compiler will detect missing or duplicate cases at compile-time.
Here is an example of the case statement that uses integer input and a range of values for each case.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure NewAge is
Age : Integer;
begin
Put ("Type you age in years: ");
Get (Age);
case Age is
when 1..19 =>
Put_Line ("You are a youngster.");
when 20..39 =>
Put_Line ("In the prime of life.");
when others =>
Put_Line ("At the peak!");
end case;
end NewAge;
And yet another example of the case statement, this one
using both integer and character input.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Calculator is
First, Second : Integer;
Operator : Character;
begin
Put ("Enter an expression: ");
Get (First);
Get (Operator);
Get (Second);
case Operator is
when '+' =>
Put (First + Second, Width => 1);
when '-' =>
Put (First - Second, Width => 1);
when '*' =>
Put (First * Second, Width => 1);
when '/' =>
Put (First / Second, Width => 1);
when others =>
Put ("Invalid operator '");
Put (Operator);
Put ("'");
end case;
New_Line;
end Calculator;
The Ada case statement is functionally identical to C's switch statement.
However, in Ada there is no need for a break or exit statement
as part of each case. Exiting the case statement is automatic
once an individual case is recognized and executed.
LoopingFor repeated execution of program statements, loops are used. If you are familiar with other programming languages you have probably used for-loops, while-loops, and until-loops. Pascal has several loop constructs, each of which is detailed below. Ada refers to looping constructs as "iteration" statements.loop statementSimple loops. Ada has a number of "looping structures" for situations where something must repeat over and over again. The simplest looping construct just repeats "forever." A simple loop begins with the phrase loop, has statements to repeat, and ends with end loop;.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greetings is
Answer : Character;
begin
loop
Put ("Is it morning (m) or afternoon (a)? ");
Get (Answer);
if Answer = 'm' or Answer = 'M' then
Put_Line ("Good morning!");
exit;
elsif Answer = 'a' or Answer = 'A' then
Put_Line ("Good afternoon!");
exit;
else
Put_Line ("You must type m or a!");
end if;
end loop;
end Greetings;
A loop can be terminated through an exit or an exit when
clause (similar to C's break statement). An exit causes an immediate
exiting of the innermost loop. An exit when clause causes exiting of
the innermost loop only if the associated condition is true.
The exit when clause is particularly useful in circumstances where you must do some action(s) before you can figure out if the loop has to stop or not. These are called "loop-and-a-half" constructs - you start with loop, perform calculations to determine if the loop should stop, use an exit when to exit on that condition, and then work on the result. A variation using the exit statement.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greetings is
Answer : Character;
begin
loop
Put("Is it morning (m) or afternoon (a)? ");
Get(Answer);
exit when Answer = 'm' or Answer = 'M'
or Answer = 'a' or Answer = 'A';
Put_Line("You must type m or a!");
end loop;
if Answer = 'm' or Answer = 'M' then
Put_Line("Good morning!");
else
Put_Line("Good afternoon!");
end if;
end Greetings;
Here is an example which puts together the if, case,
and loop statements.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Calculator is
Result : Integer;
Operator : Character;
Operand : Integer;
begin
Put ("Enter an expression: ");
Get (Result);
loop
loop
Get (Operator);
exit when Operator /= ' ';
end loop;
if Operator = '.'then
Put (Result, Width => 1);
exit;
else
Get (Operand);
case Operator is
when '+' =>
Result := Result + Operand;
when '-' =>
Result := Result - Operand;
when '*' =>
Result := Result * Operand;
when '/' =>
Result := Result / Operand;
when others =>
Put ("Invalid operator '");
Put (Operator);
Put ("'");
exit;
end case;
end if;
end loop;
New_Line;
end Calculator;
exception handlingIf an error occurs during runtime some action must be taken, or the program will fail. Ada provides the programmer with the ability to handle runtime errors and avoid program termination. When a runtime error occurs, control of the program can jump to a special branch that deals with the error. This branch is called an exception. The program below illustrates the syntax for implementing an exception branch. In this case, we are expecting the user of the program to enter an integer. If they enter any other type, it is treated as an exception.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Get_Integer is
X : Integer;
begin
loop
begin
Put ("Enter an integer: ");
Get (X);
exit;
exception
when Constraint_Error | Data_Error =>
Put ("Error in input --");
Put_Line (" please try again.");
Skip_Line;
end;
end loop;
Put ("The value entered was ");
Put (X, Width=>1);
New_Line;
end Get_Integer;
Since the programmer has no control over the user, entering the wrong
type of value in response to an input request is very common. Good programming
technique always accounts for this possibility.
while-loopsThe while loop is particularly easy. Write a normal loop block, as you saw in the previous section, and put in front of the block the keyword while and a Boolean condition. A while loop repeatedly executes the statements in the loop as long as the while condition is true.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure LoopDemo is
Count : INTEGER;
begin
Count := 1;
while Count < 5 loop
Put("Count =");
Put(Count, 5); New_Line;
Count := Count + 1;
end loop;
end LoopDemo;
Ada while loops check their conditions before executing each loop.
That means that the loop can conceivably execute "zero" times if the loop condition
starts as false.
for-loopsThe for loop is similar to the while loop, a basic
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure LoopDemo is
Index, Count : INTEGER;
begin
-- This is the for loop
for Index in 1..4 loop
Put("Doubled index =");
Put(2 * Index, 5); New_Line;
end loop;
-- This is the reverse for loop
for Count in reverse 5..8 loop
Put("Triple count =");
Put(3 * Count, 5); New_Line;
end loop;
-- An empty loop
for Index in 7..11 loop
null;
end loop;
end LoopDemo;
There are some key points about for loops that need mentioning:
ArraysAn array allows us to declare a whole set of variables of the same type in a single declaration. Furthermore, it allows us to manipulate all of the data as a single entity, and access each variable (called an array element or component) within the array individually by means of an index (also called a subscript). This means that the time taken and complexity of writing programs that use many variables of the same type is reduced. In some ways, arrays are to data structures what loops are to control structures.An array type in Ada can contain many components with the same subtype. An Ada array is quite similar to arrays in many other languages, but here are some important things to know about them:
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure ArrayMain is
Index : integer := 0;
-- fixed bounds
type Arr is array
(Integer range 1..10) of Integer;
-- variable bounds
type Aru is array
(Integer range <>) of Integer;
-- bounds are 1..10
M : Arr;
-- bounds must be given in this case
N : Aru (1..10);
begin
M(3) := 4;
N(2) := M(1) * 3;
-- print out the array elements
for Index in 1..10 loop
Put(M(Index));
New_Line;
end loop;
New_Line(2);
for Index in 1..10 loop
Put(N(Index));
New_Line;
end loop;
end ArrayMain;
Compile an run the above program. Take note of the values printed for each
of the array elements, especially those that were not initialized.
One-dimensional arraysTo define an array, we use the reserved words array and of with the appropriate modifiers (as illustrated in the example below). We define a range which the array will cover, the type of the range variable (which must be composed of discrete type limits) and the type of each element of the array. Remember that a discrete type is any type of the integer class including enumerated types.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Array1 is
N : INTEGER := 10;
Dummy1:array(INTEGER range 1..7) of BOOLEAN;
Dummy2:array(INTEGER range -21..N) of BOOLEAN;
Dummy3:array(-21..N) of BOOLEAN;
type MY_ARRAY is array(1..5) of INTEGER;
Total : MY_ARRAY;
First : MY_ARRAY;
Second : MY_ARRAY;
Funny : array(1..5) of INTEGER;
X,Y : array(12..27) of INTEGER;
Fourth_Value : INTEGER renames First(4);
begin
First(1) := 12;
First(2) := 16;
First(3) := First(2) - First(1);
Fourth_Value := -13;
First(5) := 16 - 2 * First(2);
for Index in 1..5 loop
Second(Index) := 3 * Index + 77;
end loop;
Total := First;
if Total = First then
Put("Both arrays are the same size and contain ");
Put_Line("the same values in all elements.");
end if;
for Index in 1..5 loop
Total(Index) := Total(Index) + Second(Index);
Funny(Index) := Total(Index) + First(6 - Index);
Put("The array values are");
Put(Total(Index), 6);
Put(Funny(Index), 6);
New_Line;
end loop;
end Array1;
Begin your analysis of the above program by considering the declaration
of the array named Dummy1. This line says that the variable named Dummy1
will have 7 elements numbered from 1 through 7, and each element will
have the ability to store the value of one BOOLEAN variable.
The individual elements of Dummy1 are referred to by the array name (variable name) followed
by a subscript in parentheses, i.e., Dummy1(1), Dummy1(2), ... to Dummy1(7).
Keep in mind that each array element is a single BOOLEAN variable.
The following example illustrates a common programming need, sorting the elements of an array. This program uses a technique known as "shuffle Sort."
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure Sort is
type Array_Type is array
(Positive range <>) of Integer;
A : Array_Type (1..10);
procedure Shuffle_Sort(X : in out Array_Type) is
Position : Positive;
Value : Integer;
begin
for I in X'First+1 .. X'Last loop
if X(I) < X(I-1) then
-- Misplaced item found: copy it
Value := X(I);
-- Scan backwards until correct
-- position is found
for J in reverse X'First .. I-1 loop
exit when X(J) < Value;
Position := J;
end loop;
-- Move intervening values along and slot
-- in saved copy of item
X(Position+1 .. I) := X(Position .. I-1);
X(Position) := Value;
end if;
end loop;
end Shuffle_Sort;
begin -- main program
Put_Line ("Enter 10 integers:");
for I in A'Range loop
Put (I, Width=>1);
Put (": ");
Get (A(I));
end loop;
Shuffle_Sort (A);
Put ("Sorted result: ");
for I in A'Range loop
Put (A(I));
end loop;
end Sort;
Using the above program as a guide, create a flowchart or pseudo code
specification for the Shuffle Sort algorithm. Your result should be
language independent, meaning that it can be coded in Ada, Pascal, C
or any other programming language.
Array Initialization & OperationsThe followiing program illustrates several methods for creating, initializing, and working with both one- and two-dimensional arrays.
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;
procedure ArrayInit is
Index, Index2 : INTEGER := 0;
type MY_ARRAY is array(1..5) of INTEGER;
Total : MY_ARRAY := (12, 27, -13, 122, 44);
First : MY_ARRAY := (1=>14, 2=>57, 3=>111,
5=>-27, 4=>21);
Another : MY_ARRAY := (2=>13, 5=>57, others=>3);
One_More : MY_ARRAY := (2..4=>13, 1 | 5=>27);
type MATRIX is array(INTEGER range 1..3,
INTEGER range 1..4) of INTEGER;
Square_Board : MATRIX := ((4, 7, 3, 5),
(3, 8, 2, 0),
(1, 5, 9, 9));
Checker_Board : MATRIX := (2=>(3, 8, 2, 0),
3=>(1, 5, 9, 9),
1=>(4, 7, 3, 5));
Chess_Board : MATRIX := (2=>(6, 16, 4, 1),
3=>(8, 7, 6, 5),
1=>(4=>187, 2=>11, 1=>7, 3=>19));
begin
Put(" Total = ");
for Index in 1..5 loop
Put(Total(Index), WIDTH=>6);
end loop;
New_Line;
Put(" First = ");
for Index in 1..5 loop
Put(First(Index), width=>6);
end loop;
New_Line;
Put(" Another = ");
for Index in 1..5 loop
Put(Another(Index), width=>6);
end loop;
New_Line;
Put("One_More = ");
for Index in 1..5 loop
Put(One_More(Index), width=>6);
end loop;
New_Line;
Put("Square_Board = ");
New_Line;
for Index in 1..3 loop
for Index2 in 1..4 loop
Put(Square_Board(Index,Index2), width=>5);
end loop;
New_Line;
end loop;
New_Line;
Put("Checker_Board = ");
New_Line;
for Index in 1..3 loop
for Index2 in 1..4 loop
Put(Checker_Board(Index,Index2), width=>5);
end loop;
New_Line;
end loop;
New_Line;
Put("Chess_Board = ");
New_Line;
for Index in 1..3 loop
for Index2 in 1..4 loop
Put(Chess_Board(Index,Index2), width=>5);
end loop;
New_Line;
end loop;
New_Line;
if Square_Board = Checker_Board then
Put_Line("Square and Checker are equal.");
else
Put_Line("Square and Checker are NOT equal.");
end if;
if Chess_Board = Square_Board then
Put_Line("Chess and Square are equal.");
else
Put_Line("Chess and Square are NOT equal.");
end if;
end ArrayInit;
Characters & StringsThe type CHARACTER is a predefined type in Ada and is defined as the printable set of ASCII characters (including a few that do not actually print). See Annex A.1 of the Ada 95 Reference Manual (ARM) for a complete list of the CHARACTER elements. All of the operations available with the enumerated type variable are available with the CHARACTER type variable. To illustrate their use, we declare two CHARACTER type variables in the program below, with the second being initialized to the letter D. Note the single quote marks which define the CHARACTER type literal to which the variable named Another is initialized. A different literal value is assigned to the variable My_Char (the letter 'A', and the two variables are compared in the if statement. Since 'A' is of a lesser ASCII value than 'D', the line of text My_Char is less than Another. is output to the display.Note:
Here is the example program:
with Ada.Text_IO;
use Ada.Text_IO;
procedure Chars is
My_Char : CHARACTER;
Another : CHARACTER := 'D';
begin
My_Char := 'A';
if My_Char < Another then
Put_Line("My_Char is less than Another.");
end if;
Put(My_Char);
Put(Another);
Put(My_Char);
Put_Line(" character output.");
My_Char := CHARACTER'SUCC(My_Char);
My_Char := CHARACTER'FIRST;
My_Char := CHARACTER'LAST;
end Chars;
The program below illustrates some of the operations that can
be done with the predefined type STRING. A string is an array of CHARACTER
type variables which is of a fixed length and starts with element number 1
or higher. The index uses type POSITIVE. Note that this program is called
instead of the more desirable name of string.adb because the
word STRING is a reserved word in Ada and using it for the program name
would make it unavailable for its correct use.
The declaration of the identifier Line declares an uninitialized string of 33 characters. The declaration of the identifier JOB declares a constant string of four elements initialized to the word John, and illustrates rather graphically that the string is composed of individual CHARACTER type elements. The declaration for Address is similar in that it declares another STRING constant that is then initialized (which all constants must be in order to be useful) to the value Anywhere, USA.
with Ada.Text_IO;
use Ada.Text_IO;
procedure String1 is
Line :STRING(1..33);
NAME :constant STRING :=('J','o','h','n');
JOB :constant STRING :="Computer Programmer";
Address :STRING(1..13) :="Anywhere, USA";
Letter :CHARACTER;
EXAMPLE1:constant STRING :="A";
EXAMPLE2:constant STRING :="";
begin
Line := "This is a test of STRINGS in Ada.";
Put(Line);
New_Line;
Put(NAME);
Put(" is a ");
Put(JOB);
Put(" and lives in ");
Put(Address);
New_Line(2);
Address(3) := 'X';
Address(4) := 'Y';
Address(10..13) := NAME(1..4);
Put(Address);
New_Line;
end String1;
It is important to note the different uses for 'J' and "A" as they apply to
STRING and CHARACTER values. Single quote for CHARACTER and double quote for
STRING.
SubprogramsAda has two types of subprograms; functions and procedures. This section illustrates the use of both subprogram types.A subprogram body defines the actual algorithm used by the subprogram. A subprogram body starts out with a subprogram specification (which is the subprogram declaration without the final semicolon) followed by the keyword is. This is followed by a declaration of local variables, the keyword begin, the statements to be executed, and then the keyword end. FunctionsThe purpose of a function is to calculate a value. This is the conventional mathematical meaning of a function. Small functions to access complex data structures are an essential feature of structured programming: Not only do they hide irrelevant parts of the data structure but they provide a cleaner interface to the outside world.Here is a declaration of a function which takes as input two values and returns a result:
function Average_Two(A, B : in Integer)
return Integer;
Note the keywords in and out; this indicates the mode of the parameter(s).
There are three possible modes:
Functions return a value using the return statement. Here is an example of a simple function: function Sum(A, B : in Integer) return Integer is Total : Integer := A; begin Total := Total + B; return Total; end Sum;The example below shows a function that computes the sum of the squares of two Integers. It works by creating a local function (in the same file) called Square:
function Sum_Squares (A,B:in Integer)
return Integer is
function Square(X : in Integer)
return Integer is
begin -- this is the beginning of Square
return X*X;
end Square;
begin -- this is the beginning of Sum_Squares
return Square(A) + Square(B);
end Sum_Squares;
As a stand-alone function, Square might appear as follows:
function Square(Arg : Integer) return Integer is begin return Arg * Arg; end Square;This function takes an Integer argument, which can only be read (there is no way for a function to modify its arguments), and returns a single result. There can be one or more return statements in the body of the function, execution must terminate by running into one of these return statements. This function could be edited into its own file, square.adb as shown below. Remember, you cannot run this function on its own, since it is not suitable for use as a main program. To use this function, we have a main program looking like: with Square; procedure Compute is A : Integer := 3; -- initializing a variable B : Integer; begin B := Square (A + 1); -- result is 16 end Compute;The with Square statement announces the intention of using this function in this procedure, and as shown we just use it in the natural way, with the argument in parentheses. It can be quite a nuisance to have every function in a separate file, especially if we use separate specifications for each function. Luckily we can avoid this, because Ada 95 provides the concept of a package to deal with collectiing together (packaging) functions into a single file. Typically we bundle a set of related functions. In this case, Cube and Square are related, so let's bundle them up into a package called Powers. For packages, it is absolutely required to have two files, one for the specification and one for the body. The specification will looks like the one illustrated below: package Powers is function Square (Arg : Integer) return Integer; function Cube (Arg : Integer) return Integer; end Powers;The specification goes in a file called powers.ads. The body, which goes in the file called powers.adb, looks like:
package body Powers is
function Square (Arg : Integer) return Integer is
begin
return Arg * Arg;
end Square;
function Cube (Arg : Integer) return Integer is
begin
return Arg * Square (Arg);
end Cube;
end Powers;
The package specification is just a series of declarations, including function specs.
Later we will see that it typically can contain type declarations and even
variable declarations that are logically related.
The package body has the bodies of the functions. These look exactly like the bodies when we compiled them separately, with one interesting exception. The with Square on the body of Cube is gone. That's because with statements belong only at the start of the unit (if Powers needed to with anything then the with statements would go before the keyword package). But more importantly, you do not need to with things that are in the same package as you are. A package is a family of declarations which can all see one another without any fiddling. To use the package, we make a main program that includes a with statement for the package. For example:
with Powers;
procedure Withp is
-- defining a constant
A : constant Integer := 10;
B : Integer;
begin
-- result is 1000
B := Powers.Cube (A); end Withp;
When we call the Cube function, we use object-oriented notation and precede
the name of the function with the package name, Powers and a period to
indicate which package the Cube function lives in, otherwise using a function
inside a package is the same as using it in the standalone case. In practice,
nearly all functions live in packages, typically only the main program lives
on its own as a separate file.
The requirement to put the package name Powers and a period can be a bit tedious. However, it does have its advantages. In a big program, it can make it easier to see where things come from. On the other hand it clutters up the text. As always in Ada 95, we favor the reader of the code over the writer, so we are not very sensitive to the argument that it is a nuisance to *write* the extra junk, but we might worry about the cluttering causing it to be harder to read. If we feel that way, we can use the use clause:
with Powers; use Powers;
procedure Usep is
A : constant Integer := 10;
B : Integer;
begin
B := Cube (A);
end Usep;
and then we do not need the prefix. The use clause makes all the items declared in the
package visible without the prefix.
To "use" or not to "use", that is the question! This is a controversial subject. Some Ada 95 programmers regard the use clause as being in the same category as a goto statement. Other Ada 95 programmers find them useful. It is up to you to decide, but try to adopt a consistent style. One additional point to note. It is perfectly valid to have the same names declared in multiple packages. In this case, you generally have to use the prefix notation anyway, so that the compiler knows which instance of a name you are talking about. ProceduresAda was designed to be very modular, and we have come to the point where we will study the first and simplest form of modularization, the procedure. Some languages call this kind of subprogram a subroutine, others a function, but Ada calls it a procedure.The main difference between a procedure and function is that a function returns a value, while a procedure does not (though a procedure can change the values of parameters sent to it). Here is a simple subprogram body that implements the procedure Average we declared in the last section. Note that after the word end we can add a word indicating what we are ending (the Ada compiler will check to make sure this is correct). Also note that the assignment statement in Ada is written as := (the same as Pascal):
procedure Average(A, B : in Integer;
Result : out Integer) is
begin
Result := (A + B) / 2;
end Average;
Local variables and local subprograms can be declared between the is and the begin.
Local variables and local subprograms exist as long as their enclosing subprogram exists.
Local variables are useful as "scratchpads" to hold intermediate results.
Local variables are written the same as parameters are: the variable name(s),
a colon, and their type.
Functions take parameters, which are read only, and compute a single result. Procedures can take parameters also, and in addition, can specify parameters as being writable as well as readable. This allows a procedure to return multiple results. Here is an example: procedure Procp (A :Integer; -- default is in Ai :in Integer; -- actual can be expression Ao :out Integer; -- actual must be variable Aio:in out Integer) -- actual must be variable is begin Ao :=A+Ai; -- read in params, write out params Aio:=Ai+Aio; -- read or write in out parames Aio:=Aio+Ao; -- read out params after setting them end Procp;This procedure could live in its own file as procp.adb, or could be placed inside a package. In either case, it would probably have a separate specification:
procedure Procp
(A : Integer;
Ai : in Integer;
Ao : out Integer;
Aio : in out Integer);
-- Fascinating comments explaining what Procp does
To call a procedure, we use the keyword with it, and then we can call it
using the technique illustrated below:
with Procp; procedure Pcall is A,B,C : Integer; begin Procp (3,6,A,B); -- named parameters Procp (Aio => B, Ao => A, A => 3, Ai => 6); end Pcall;As shown here, procedures can be called using positional notation, in which we have to remember the order of the parameters, or the more useful named notation, in which we can put the parameters in any order. Named notation should almost always be used except in a very simple case. For example: V := Square(Q);seems OK. Although we could write a named parameter version: V := Square(Arg=>Q);that illustrates how to include the name of the parameter in the call. However, because the name chosen is not descriptive, even this does not help much. The following example shows a better approach:
Integrate(Func=>Cosine, From=>0.12,
To=>0.15, Result=>Area);
is preferable to the more cryptic version below:
Integrate(Func,0.12,0.15,Area);Note how the careful choice of parameter names makes the procedure call read well so that you can better understand what it does without needing to look at the specification. Carefully designing your functions and procedures (collectively called subprograms in Ada 95) to work this way will ease the job of anyone who has to read your code (including you!).
| |||||
| |
| Added to the Web: October 24, 1999. Last updated: October 27, 1999. Web page design by Dan Solarek. |
![]() http://cset.sp.utoledo.edu/cset4250/ |