5.5 Expressions
Properly coded expressions can enhance the readability and understandability of a program. Poorly coded expressions can turn a program into a maintainer's nightmare.
Range Values
guideline
- Use
'First
or'Last
instead of numeric literals to represent the first or last values of a range. - Use
'Range
or the subtype name of the range instead of'First .. 'Last
.
example
type Temperature is range All_Time_Low .. All_Time_High; type Weather_Stations is range 1 .. Max_Stations; Current_Temperature : Temperature := 60; Offset : Temperature; ... for I in Weather_Stations loop Offset := Current_Temperature - Temperature'First; ... end loop;
rationale
In the example above, it is better to use Weather_Stations
in the
for
loop than to use Weather_Stations'First
.. Weather_Stations'Last
or 1 .. Max_Stations
because it is clearer,
less error-prone, and less dependent on the definition of the type
Weather_Stations
. Similarly, it is better to use Temperature'First
in the offset calculation than to use All_Time_Low
because the code
will still be correct if the definition of the subtype Temperature
is
changed. This enhances program reliability.
caution
When you implicitly specify ranges and attributes like this, be careful that you use the correct subtype name. It is easy to refer to a very large range without realizing it. For example, given the declarations:
type Large_Range is new Integer; subtype Small_Range is Large_Range range 1 .. 10;
type Large_Array is array (Large_Range) of Integer; type Small_Array is array (Small_Range) of Integer;
then the first declaration below works fine, but the second one is probably an accident and raises an exception on most machines because it is requesting a huge array (indexed from the smallest integer to the largest one):
Array_1 : Small_Array; Array_2 : Large_Array;
Array Attributes
guideline
- Use array attributes
'First
,'Last
, or'Length
instead of numeric literals for accessing arrays. - Use the
'Range
of the array instead of the name of the index subtype to express a range. - Use
'Range
instead of'First .. 'Last
to express a range.
example
subtype Name_String is String (1 .. Name_Length); File_Path : Name_String := (others => ' '); ... for I in File_Path'Range loop ... end loop;
rationale
In the example above, it is better to use Name_String'Range
in the
for
loop than to use Name_String_Size
, Name_String'First .. Name_String'Last
, or 1 .. 30
because it is clearer, less error-prone,
and less dependent on the definitions of Name_String
and
Name_String_Size
. If Name_String
is changed to have a different
index type or if the bounds of the array are changed, this will still
work correctly. This enhances program reliability.
Parenthetical Expressions
guideline
- Use parentheses to specify the order of subexpression evaluation to clarify expressions (NASA 1987 ).
- Use parentheses to specify the order of evaluation for subexpressions whose correctness depends on left to right evaluation.
example
(1.5 * X**2)/A - (6.5X + 47.0) 2I + 4Y + 8Z + C
rationale
The Ada rules of operator precedence are defined in the Ada Reference Manual (1995, §4.5) and follow the same commonly accepted precedence of algebraic operators. The strong typing facility in Ada combined with the common precedence rules make many parentheses unnecessary. However, when an uncommon combination of operators occurs, it may be helpful to add parentheses even when the precedence rules apply. The expression:
5 + ((Y ** 3) mod 10)
is clearer, and equivalent to:
5 + Y**3 mod 10
The rules of evaluation do specify left to right evaluation for operators with the same precedence level. However, it is the most commonly overlooked rule of evaluation when checking expressions for correctness.
Positive Forms of Logic
guideline
- Avoid names and constructs that rely on the use of negatives.
- Choose names of flags so they represent states that can be used in positive form.
example
Use:
if Operator_Missing then
rather than either:
if not Operator_Found then
or:
if not Operator_Missing then
rationale
Relational expressions can be more readable and understandable when stated in a positive form. As an aid in choosing the name, consider that the most frequently used branch in a conditional construct should be encountered first.
exceptions
There are cases in which the negative form is unavoidable. If the relational expression better reflects what is going on in the code, then inverting the test to adhere to this guideline is not recommended.
Short Circuit Forms of the Logical Operators
guideline
- Use short-circuit forms of the logical operators to specify the order of conditions when the failure of one condition means that the other condition will raise an exception.
example
Use:
if Y /= 0 or else (X/Y) /= 10 then
or:
if Y /= 0 then if (X/Y) /= 10 then
rather than either:
if Y /= 0 and (X/Y) /= 10 then
or:
if (X/Y) /= 10 then
to avoid Constraint_Error.
Use:
if Target /= null and then Target.Distance < Threshold then
rather than:
if Target.Distance < Threshold then
to avoid referencing a field in a nonexistent object.
rationale
The use of short-circuit control forms prevents a class of
data-dependent errors or exceptions that can occur as a result of
expression evaluation. The short-circuit forms guarantee an order of
evaluation and an exit
from the sequence of relational expressions as
soon as the expression's result can be determined.
In the absence of short-circuit forms, Ada does not provide a guarantee
of the order of expression evaluation, nor does the language guarantee
that evaluation of a relational expression is abandoned when it becomes
clear that it evaluates to False
(for and
) or True
(for or
).
notes
If it is important that all parts of a given expression always be evaluated, the expression probably violates Guideline 4.1.4 , which limits side-effects in functions.
Accuracy of Operations With Real Operands
guideline
- Use
<=
and>=
in relational expressions with real operands instead of=
.
example
Current_Temperature : Temperature := 0.0; Temperature_Increment : Temperature := 1.0 / 3.0; Maximum_Temperature : constant := 100.0; ... loop ... Current_Temperature := Current_Temperature + Temperature_Increment; ... exit when Current_Temperature >= Maximum_Temperature; ... end loop;
rationale
Fixed- and floating-point values, even if derived from similar expressions, may not be exactly equal. The imprecise, finite representations of real numbers in hardware always have round-off errors so that any variation in the construction path or history of two real numbers has the potential for resulting in different numbers, even when the paths or histories are mathematically equivalent.
The Ada definition of model intervals also means that the use of <=
is
more portable than either <
or =
.
notes
Floating-point arithmetic is treated in Guideline 7.2.7 .
exceptions
If your application must test for an exact value of a real number (e.g.,
testing the precision of the arithmetic on a certain machine), then the
=
would have to be used. But never use =
on real operands as a
condition to exit a loop .