Imperative language¶
Ada is a multi-paradigm language with support for object orientation and some elements of functional programming, but its core is a simple, coherent procedural/imperative language akin to C or Pascal.
In other languages
One important distinction between Ada and a language like C is that
statements and expressions are very clearly distinguished. In Ada, if you
try to use an expression where a statement is required then your program
will fail to compile. This rule supports a useful stylistic principle:
expressions are intended to deliver values, not to have side effects. It
can also prevent some programming errors, such as mistakenly using the
equality operator = instead of the assignment operation := in
an assignment statement.
Hello world¶
Here's a very simple imperative Ada program:
with Ada.Text_IO;
procedure Greet is
begin
-- Print "Hello, World!" to the screen
Ada.Text_IO.Put_Line ("Hello, World!");
end Greet;
which we'll assume is in the source file greet.adb.
If you compile that source with the GNAT compiler and run the executable, you will get an unsurprising result.
$ gprbuild greet.adb
using project file [...]_default.gpr
Compile
[Ada] greet.adb
Bind
[gprbind] greet.bexch
[Ada] greet.ali
Link
[link] greet.adb
$ ./greet
Hello, World!
$
There are several noteworthy things in the above program:
A subprogram in Ada can be either a procedure or a function. A procedure, as illustrated above, does not return a value when called.
withis used to reference external modules that are needed in the procedure. This is similar toimportin various languages or roughly similar to#includein C and C++. We'll see later how they work in detail. Here, we are requesting a standard library module, theAda.Text_IOpackage, which contains a procedure to print text on the screen:Put_Line.Greetis a procedure, and the main entry point for our first program. Unlike in C or C++, it can be named anything you prefer. The builder will determine the entry point. In our simple example, gprbuild, GNAT's builder, will use the file you passed as parameter.Put_Lineis a procedure, just likeGreet, except it is declared in theAda.Text_IOmodule. It is the Ada equivalent of C'sprintf.Comments start with
--and go to the end of the line. There is no multi-line comment syntax, that is, it is not possible to start a comment in one line and continue it in the next line. The only way to create multiple lines of comments in Ada is by using--on each line. For example:
-- We start a comment in this line...
-- and we continue on the second line...
In other languages
Procedures are similar to functions in C or C++ that return void.
We'll see later how to declare functions in Ada.
Here is a minor variant of the "Hello, World" example:
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet is
begin
-- Print "Hello, World!" to the screen
Put_Line ("Hello, World!");
end Greet;
This version utilizes an Ada feature known as a use clause, which has
the form use package-name. As illustrated by the call on
Put_Line, the effect is that entities from the named package can be
referenced directly, without the package-name. prefix.
Imperative language - If/Then/Else¶
This section describes Ada's if statement and introduces several other
fundamental language facilities including integer I/O, data declarations,
and subprogram parameter modes.
Ada's if statement is pretty unsurprising in form and function:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Check_Positive is
N : Integer;
begin
-- Put a String
Put ("Enter an integer value: ");
-- Read in an integer value
Get (N);
if N > 0 then
-- Put an Integer
Put (N);
Put_Line (" is a positive number");
end if;
end Check_Positive;
The if statement minimally consists of the reserved word if, a
condition (which must be a Boolean value), the reserved word then and a
non-empty sequence of statements (the then part) which is executed if the
condition evaluates to True, and a terminating end if.
This example declares an integer variable N, prompts the user for an integer, checks if the value is positive and, if so, displays the integer's value followed by the string " is a positive number". If the value is not positive, the procedure does not display any output.
The type Integer is a predefined signed type, and its range depends on the computer architecture. On typical current processors Integer is 32-bit signed.
The example illustrates some of the basic functionality for integer input-output.
The relevant subprograms are in the predefined package
Ada.Integer_Text_IO and include the Get procedure (which reads in
a number from the keyboard) and the Put procedure (which displays an
integer value).
Here's a slight variation on the example, which illustrates an if statement
with an else part:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Check_Positive is
N : Integer;
begin
-- Put a String
Put ("Enter an integer value: ");
-- Reads in an integer value
Get (N);
-- Put an Integer
Put (N);
if N > 0 then
Put_Line (" is a positive number");
else
Put_Line (" is not a positive number");
end if;
end Check_Positive;
In this example, if the input value is not positive then the program displays the value followed by the String " is not a positive number".
Our final variation illustrates an if statement with elsif
sections:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Check_Direction is
N : Integer;
begin
Put ("Enter an integer value: ");
Get (N);
Put (N);
if N = 0 or N = 360 then
Put_Line (" is due north");
elsif N in 1 .. 89 then
Put_Line (" is in the northeast quadrant");
elsif N = 90 then
Put_Line (" is due east");
elsif N in 91 .. 179 then
Put_Line (" is in the southeast quadrant");
elsif N = 180 then
Put_Line (" is due south");
elsif N in 181 .. 269 then
Put_Line (" is in the southwest quadrant");
elsif N = 270 then
Put_Line (" is due west");
elsif N in 271 .. 359 then
Put_Line (" is in the northwest quadrant");
else
Put_Line (" is not in the range 0..360");
end if;
end Check_Direction;
This example expects the user to input an integer between 0 and 360
inclusive, and displays which quadrant or axis the value corresponds
to. The in operator in Ada tests whether a scalar value is
within a specified range and returns a Boolean result.
The effect of the program should be self-explanatory; later we'll see an
alternative and more efficient style to accomplish the same effect,
through a case statement.
Ada's elsif keyword differs from C or
C++, where nested else .. if blocks would be used instead.
And another difference is the presence of the end if in Ada,
which avoids the problem known as the "dangling else".
Imperative language - Loops¶
Ada has three ways of specifying loops. They differ from the C / Java / Javascript for-loop, however, with simpler syntax and semantics in line with Ada's philosophy.
For loops¶
The first kind of loop is the for loop, which allows iteration through a
discrete range.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet_5a is
begin
for I in 1 .. 5 loop
-- Put_Line is a procedure call
Put_Line ("Hello, World!"
& Integer'Image (I));
-- ^ Procedure parameter
end loop;
end Greet_5a;
Executing this procedure yields the following output:
Hello, World! 1
Hello, World! 2
Hello, World! 3
Hello, World! 4
Hello, World! 5
A few things to note:
1 .. 5is a discrete range, from1to5inclusive.The loop parameter
I(the name is arbitrary) in the body of the loop has a value within this range.Iis local to the loop, so you cannot refer toIoutside the loop.Although the value of
Iis incremented at each iteration, from the program's perspective it is constant. An attempt to modify its value is illegal; the compiler would reject the program.
Integer'Imageis a function that takes an Integer and converts it to aString. It is an example of a language construct known as an attribute, indicated by the'syntax, which will be covered in more detail later.The
&symbol is the concatenation operator for String valuesThe
end loopmarks the end of the loop
The "step" of the loop is limited to 1 (forward direction) and -1 (backward).
To iterate backwards over a range, use the reverse keyword:
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet_5a_Reverse is
begin
for I in reverse 1 .. 5 loop
Put_Line ("Hello, World!"
& Integer'Image (I));
end loop;
end Greet_5a_Reverse;
Executing this procedure yields the following output:
Hello, World! 5
Hello, World! 4
Hello, World! 3
Hello, World! 2
Hello, World! 1
The bounds of a for loop may be computed at run-time; they
are evaluated once, before the loop body is executed. If the value of the
upper bound is less than the value of the lower bound, then the
loop is not executed at all. This is the case also for reverse loops.
Thus no output is produced in the following example:
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet_No_Op is
begin
for I in reverse 5 .. 1 loop
Put_Line ("Hello, World!"
& Integer'Image (I));
end loop;
end Greet_No_Op;
The for loop is more general than what we illustrated here;
more on that later.
Bare loops¶
The simplest loop in Ada is the bare loop, which forms the foundation of the other kinds of Ada loops.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet_5b is
-- Variable declaration:
I : Integer := 1;
-- ^ Type
-- ^ Initial value
begin
loop
Put_Line ("Hello, World!"
& Integer'Image (I));
-- Exit statement:
exit when I = 5;
-- ^ Boolean condition
-- Assignment:
I := I + 1;
-- There is no I++ short form to
-- increment a variable
end loop;
end Greet_5b;
This example has the same effect as Greet_5a shown earlier.
It illustrates several concepts:
We have declared a variable named
Ibetween theisand thebegin. This constitutes a declarative region. Ada clearly separates the declarative region from the statement part of a subprogram. A declaration can appear in a declarative region but is not allowed as a statement.The bare loop statement is introduced by the keyword
loopon its own and, like every kind of loop statement, is terminated by the combination of keywordsend loop. On its own, it is an infinite loop. You can break out of it with anexitstatement.The syntax for assignment is
:=, and the one for equality is=. There is no way to confuse them, because as previously noted, in Ada, statements and expressions are distinct, and expressions are not valid statements.
While loops¶
The last kind of loop in Ada is the while loop.
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet_5c is
I : Integer := 1;
begin
-- Condition must be a Boolean value
-- (no Integers).
-- Operator "<=" returns a Boolean
while I <= 5 loop
Put_Line ("Hello, World!"
& Integer'Image (I));
I := I + 1;
end loop;
end Greet_5c;
The condition is evaluated before each iteration. If the result is false, then the loop is terminated.
This program has the same effect as the previous examples.
In other languages
Note that Ada has different semantics than C-based languages with respect
to the condition in a while loop. In Ada the condition has to be a Boolean
value or the compiler will reject the program; the condition is not an
integer that is treated as either True or False depending on
whether it is non-zero or zero.
Imperative language - Case statement¶
Ada's case statement is similar to the C and C++ switch statement,
but with some important differences.
Here's an example, a variation of a program that was shown earlier
with an if statement:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Check_Direction is
N : Integer;
begin
loop
Put ("Enter an integer value: ");
Get (N);
Put (N);
case N is
when 0 | 360 =>
Put_Line
(" is due north");
when 1 .. 89 =>
Put_Line
(" is in the northeast quadrant");
when 90 =>
Put_Line
(" is due east");
when 91 .. 179 =>
Put_Line
(" is in the southeast quadrant");
when 180 =>
Put_Line
(" is due south");
when 181 .. 269 =>
Put_Line
(" is in the southwest quadrant");
when 270 =>
Put_Line
(" is due west");
when 271 .. 359 =>
Put_Line
(" is in the northwest quadrant");
when others =>
Put_Line
(" Au revoir");
exit;
end case;
end loop;
end Check_Direction;
This program repeatedly prompts for an integer value and then, if the value is
in the range 0 .. 360, displays the associated quadrant or axis. If the
value is an Integer outside this range, the loop (and the program) terminate
after outputting a farewell message.
The effect of the case statement is similar to the if statement in an earlier example, but the case statement can be more efficient because it does not involve multiple range tests.
Notable points about Ada's case statement:
The case expression (here the variable
N) must be of a discrete type, i.e. either an integer type or an enumeration type. Discrete types will be covered in more detail later discrete types.Every possible value for the case expression needs to be covered by a unique branch of the case statement. This will be checked at compile time.
A branch can specify a single value, such as
0; a range of values, such as1 .. 89; or any combination of the two (separated by a |).As a special case, an optional final branch can specify
others, which covers all values not included in the earlier branches.Execution consists of the evaluation of the case expression and then a transfer of control to the statement sequence in the unique branch that covers that value.
When execution of the statements in the selected branch has completed, control resumes after the
end case. Unlike C, execution does not fall through to the next branch. So Ada doesn't need (and doesn't have) abreakstatement.
Imperative language - Declarative regions¶
As mentioned earlier, Ada draws a clear syntactic separation between declarations, which introduce names for entities that will be used in the program, and statements, which perform the processing. The areas in the program where declarations may appear are known as declarative regions.
In any subprogram, the section between the is and the begin is a
declarative region. You can have variables, constants, types, inner subprograms,
and other entities there.
We've briefly mentioned variable declarations in previous subsection. Let's look
at a simple example, where we declare an integer variable X in the
declarative region and perform an initialization and an addition on it:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
X : Integer;
begin
X := 0;
Put_Line ("The initial value of X is "
& Integer'Image (X));
Put_Line ("Performing operation on X...");
X := X + 1;
Put_Line ("The value of X now is "
& Integer'Image (X));
end Main;
Let's look at an example of a nested procedure:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
procedure Nested is
begin
Put_Line ("Hello World");
end Nested;
begin
Nested;
-- Call to Nested
end Main;
A declaration cannot appear as a statement. If you need to declare a local variable amidst the statements, you can introduce a new declarative region with a block statement:
with Ada.Text_IO; use Ada.Text_IO;
procedure Greet is
begin
loop
Put_Line ("Please enter your name: ");
declare
Name : String := Get_Line;
-- ^ Call to the
-- Get_Line function
begin
exit when Name = "";
Put_Line ("Hi " & Name & "!");
end;
-- Name is undefined here
end loop;
Put_Line ("Bye!");
end Greet;
Attention
The Get_Line function allows you to receive input from the user, and
get the result as a string. It is more or less equivalent to the scanf
C function.
It returns a String, which, as we will see later, is an
Unconstrained array type. For now we
simply note that, if you wish to declare a String variable and do
not know its size in advance, then you need to initialize the variable
during its declaration.
Imperative language - conditional expressions¶
Ada 2012 introduced an expression analog for conditional statements
(if and case).
If expressions¶
Here's an alternative version of an example we saw earlier; the if
statement has been replaced by an if expression:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
procedure Check_Positive is
N : Integer;
begin
Put ("Enter an integer value: ");
Get (N);
Put (N);
declare
S : constant String :=
(if N > 0
then " is a positive number"
else " is not a positive number");
begin
Put_Line (S);
end;
end Check_Positive;
The if expression evaluates to one of the two Strings depending
on N, and assigns that value to the local variable S.
Ada's if expressions are similar to if statements. However,
there are a few differences that stem from the fact that it is an expression:
All branches' expressions must be of the same type
It must be surrounded by parentheses if the surrounding expression does not already contain them
An
elsebranch is mandatory unless the expression followingthenhas a Boolean value. In that case anelsebranch is optional and, if not present, defaults toelse True.
Here's another example:
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
begin
for I in 1 .. 10 loop
Put_Line (if I mod 2 = 0
then "Even"
else "Odd");
end loop;
end Main;
This program produces 10 lines of output, alternating between "Odd" and "Even".
Case expressions¶
Analogous to if expressions, Ada also has case expressions.
They work just as you would expect.
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
begin
for I in 1 .. 10 loop
Put_Line
(case I is
when 1 | 3 | 5 | 7 | 9 => "Odd",
when 2 | 4 | 6 | 8 | 10 => "Even");
end loop;
end Main;
This program has the same effect as the preceding example.
The syntax differs from case statements, with branches separated
by commas.