Previous | Table of Contents | Next |
Returning to the example, each of the six declarations includes an initial expression, which is evaluated at runtime when the declaration is elaborated. Note that each of these expressions is dynamic, depending on the current clock value. The expression
Natural(Ada.Calendar.Seconds(Now))
returns the Seconds component converted from Day_Duration to Natural. Duration is actually a fixed-point type, not an integer one, so fractions of seconds can be represented. Converting to Natural truncates the value, discarding the fractional part; I am interested only in whole seconds here.
The expressions
SecsPast_0h00 / 60 SecsPast_0h00 rem 60
are integer expressions, computing the quotient and the remainder respectively. In Ada, the interpretation of arithmetic operators, including division, depends on the types of their operands. SecsPast_0h00 (seconds since midnight) and 60 are both integer values, so the division is a truncating one.
There is no C-style promotion or implicit conversion in Ada; for example, mixed integer/real expressions are rejected by the compiler. One can use explicit type conversions to adjust the types in expressions that would otherwise be mixed. The requirement for explicit type conversion ensures that the result of an arithmetic expression is obvious to the human reader.
In the example, because the values of all the variables are computed at the time their declarations are elaborated, the statement-sequence part of the program need only display the result. Lines 19-23 display the date just as before; the rest of the program displays the time in 24-hour hh:mm:ss form. The if statements in lines 25-27, 31-33, and 37-39 are used to display leading zeroes where necessary, for example, 09:15:08. if statements are fully bracketed; that is, the if must have a closing end if. An if can include a single else or one or more elsif clauses. Two other if variations are
if ... then ... else ... end if; if ... then ... elsif ... then ... elsif ... then ... else ... end if;
Lines 28, 34, and 40 are calls to Ada.Integer_Text_IO.Put. Width=>1 is a formatting parameter that requests at least one position for the output value. A value of one or more digits is displayed properly; the effect of this width parameter is to left-adjust the value.
Width is a parameter that is defined with a default value so that a call to Put can omit Width. The default width is one larger than the width of IntegerLast, typically 11.
Note in passing that the integer Put has yet another parameter, Base, which defaults to 10 and indicates the desired number base (bases in the range 2..16 are allowed). The call
Ada.Integer_Text_IO(Item => 19, Base => 8);
displays the octal value 8#23# (base first, then value delimited by # signs); substituting Base=>2 displays 2#10011#. Using this syntax, it is possible to format input values in non-decimal bases. Again you see that Ada provides simple defaults with much capability to override these.
10.4.1.8. The Brevity/Clarity Tradeoff
Ada has been criticized for its apparent verbosity. Adas proponents respond that Ada was designed for the development and maintenance of programs of nontrivial size with long life cycles. Such programs are written once but read and modified many times over their lives, so the language definitely favors the reader over the writer, and Ada programmers tend to become conditioned to write less for their own eyes than for others. This courtesy to the reader of a program is not considered to be a disadvantage!
On the other hand, it is possible to write Ada more tersely. Single-character variable names are legal and the language does not compel the use of whitespace. Also, as I observed earlier, you can add use clauses and employ positional parameters instead of named ones. Consider Show_Use_Clause, which is a terse version of Show_Date_and_Time. If it were a program of a few thousand lines instead of 44 lines, making reference to 5 or 10 different packages, the reader would probably miss the explicit package and formal parameter names.
1 with Ada.Text_IO, Ada.Integer_Text_IO; 2 use Ada.Text_IO, Ada.Integer_Text_IO; 3 with Ada.Calendar; 4 use Ada.Calendar; 5 procedure Show_Use_Clause is 6 7 type Months is 8 (Jan, Feb, Mar, Apr, May, Jun, 9 Jul, Aug, Sep, Oct, Nov, Dec); 10 11 Now : Time := Clock; 12 SecsPast_0h00: Natural := Natural(Seconds(Now)); 13 MinsPast_0h00: Natural := SecsPast_0h00/60; 14 Secs : Natural := SecsPast_0h00 rem 60; 15 Mins : Natural := MinsPast_0h00 rem 60; 16 Hrs : Natural := MinsPast_0h00 / 60; 17 18 begin 19 20 Put(The date and time is 21 & IntegerImage(Day(Now)) & 22 & MonthsImage (MonthsVal(Month(Now) - 1)) 23 & IntegerImage(Year(Now)) & ); 24 25 if Hrs < 10 then 26 Put (0); 27 end if; 28 Put (Hrs, 1); 29 Put (:); 30 31 if Mins < 10 then 32 Put (0); 33 end if; 34 Put (Mins, 1); 35 Put (:); 36 37 if Secs < 10 then 38 Put (0); 39 end if; 40 Put (Secs, 1); 41 42 New_Line; 43 44 end Show_Use_Clause;
Note that in this program, there are calls to three different Put procedures: one for single characters, one for strings, and one for integers. This is an example of overloading, or giving the same name to several different subprograms. Overloading is quite common in Ada; the compiler readily distinguishes the three procedures by examining the profile (order, number, and type) of the parameters.
If, by some chance, two different procedures with the same name and the same parameter profile were made directly visible by use clauses (this could happen if the procedures were in different packages), the compiler would discover the ambiguity and give an error message. The programmer could easily correct the ambiguity by prefixing the package names.
I have designed the examples here to explicate the language and some of its standard libraries; therefore, I generally use a verbose style for maximum clarity. Occasionally, I add use clauses and omit parameter names where there is no loss of clarity. Programming style issues should really be managed by each project team.
Previous | Table of Contents | Next |