The statements was interesting, but tough.
Mark Twain, The Adventures of Huckleberry Finn
Lets try a slightly more elaborate example than the ones weve seen so far. This one asks the user whether its morning or afternoon and then replies Good morning or Good afternoon as appropriate. Heres what it looks like:
with Ada.Text_IO; use Ada.Text_IO; procedure Greetings is Answer : Character; -- 1 begin Put ("Is it morning (m) or afternoon (a)? "); -- 2 Get (Answer); -- 3 if Answer = 'm' then -- 4 Put_Line ("Good morning!"); -- 5 else -- 6 Put_Line ("Good afternoon!"); -- 7 end if; -- 8 end Greetings; -- 9
Line 1 declares a variable called Answer to hold the answer that the user types in response to the question asked on line 2. The answer will be a single character ('m' or 'a'), so Ive declared it to be a variable of type Character, which is another standard data type. Character variables are capable of holding a single character. The answer is read in using a version of Get defined in Ada.Text_IO which has an output parameter of type Character.
Lines 4 through 8 are an if statement which allows us to choose between two alternative courses of action. Like a procedure definition it is a compound construction with a semicolon at the very end of it on line 8 and no semicolons on either line 4 or 6. Compound constructions like this always end with end whatever-it-is in Ada, so that procedure X ends with end X and if ends with end if. The statements on lines 5 and 7 are indented to make it visually obvious that they are enclosed by the if statement.
When the if statement is executed, the condition after the word if is tested and, if it is true, the statements between then and else are executed, which in this case is the single statement on line 5. If the condition is false, the statements between else and end if (in this case the single statement on line 7) will be executed. The effect is that if the value of Answer is the letter m, the message Good morning! is displayed, otherwise the message Good afternoon! is displayed. Once the if statement has been executed (i.e. either line 5 or line 7 has been executed), execution continues with whatever follows the if statement. In this case it is line 9, which is the end of the program.
Note that you have to use single quote marks to enclose characters; 'm' is the letter m as a value of type Character, whereas "m" is a string which is one character in length. The compiler takes this apparently trivial difference quite seriously, since the particular style of quote is used to distinguish values of type Character from values of type String, which in turn determines what operations can legitimately be performed on them.
In its present form the program doesnt quite do what we really want. If you type anything other than the letter m the program will respond with a cheery Good afternoon!. Ideally we would like to check that the answer is in fact an a if it is not an m and display a less cheery error message if it isnt. We can do this by using another if statement instead of the existing line 7:
if Answer = 'a' then Put_Line ("Good afternoon!"); else Put_Line ("Please type 'm' or 'a'!"); end if;
The complete if statement starting at line 4 now looks like this:
if Answer = 'm' then Put_Line ("Good morning!"); else if Answer = 'a' then Put_Line ("Good afternoon!"); else Put_Line ("Please type 'm' or 'a'!"); end if; end if;
This shows that the statements contained within an if statement can be any statements at all, including further if statements. Note that each if statement has its own corresponding end if. If you have a lot of if statements nested inside one another you can end up with an awful lot of end ifs, as well as indentation problems as the if statements are going to be indented further and further to the right. To get around this, we can write the if statement above a different way:
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;
The reserved word elsif allows you to specify a secondary condition as part of the same if statement. Since its all a single if statement now, only a single end if is required at the very end and there is no problem with indentation. You can have as many elsif parts in an if statement as you want, but there can only ever be one else part which must come at the very end and is only executed if all the conditions specified after if and elsif are false. You can also leave out the else part completely if you dont want to do anything when the conditions you specify are all false.
Note the missing e in elsif! A common beginners mistake is to spell it elseif, but the spelling was deliberately chosen so that if the words else and if accidentally get run together as elseif as a result of missing out the space between the two words, the compiler will immediately spot it as an error.
At the moment you have to type in the letter m or a in lower case in response to the prompt. If you type in a capital M instead, the program will respond Please type m or a!. We can modify the program to test for upper case letters and convert them to the lower case equivalents by adding an extra 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 -- 1 Answer := 'm'; -- 2 elsif Answer = 'A' then -- 3 Answer := 'a'; -- 4 end if; -- 5 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 if statement on lines 1 to 5 checks for the letter M or A and changes the value of Answer to m or a as appropriate. It does this by assigning a new value to Answer on lines 2 and 4. The assignment statement
Answer := 'm';
stores the letter m into the variable Answer, replacing the existing value of Answer. The symbol := is usually pronounced becomes, so we can read this statement as Answer becomes m. You must give a variable name on the left of :=, but you can have any expression you like on the right hand side as long as it produces a value of the correct type when its evaluated.
Note that there is no else part in this if statement. If Answer is the letter M, line 2 is executed; if it is the letter A, line 4 is executed; and if it is anything else, we dont do anything. As I mentioned earlier, leaving out the else part of an if statement simply means that you do nothing if none of the conditions specified after if and elsif are true.
There is another way to do the same thing. We can eliminate the extra if statement and alter the second one to test if Answer is either an M or an m as follows:
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;
The first line of this revised if statement checks if Answer is an m and also checks if it is an M. If either condition is true the message Good morning! is displayed.
The or operator allows us to combine more than one condition into a single compound condition which is true if either or both of the subconditions are true. It is tempting to try to write the first line of the if statement as follows:
if Answer = 'm' or 'M' then ...
but the compiler will complain if you do.
The reason is that or requires something which evaluates to either true or false (a Boolean expression) on both its left and its right hand sides. Boolean is another one of Adas built-in types, and is named after the English logician George Boole who first formalised the notion of an algebra of truth values. The = operator compares two values of the same type and produces a Boolean result (true or false) so all will be well as long as you use an expression of the form A = B on both sides of the or operator. The condition above has a Boolean expression on its left but a value of type Character on its right, so the compiler will be justifiably sceptical about it. Ada compilers are very strict about type-checking since confusion about types generally indicates muddled thinking on the part of the programmer; the Ada view of data types will be explored in more detail later on.
When there are a lot of alternative values of the same variable to be dealt with, its generally more convenient to use a case statement than an if statement. Heres how the previous program could be rewritten using a 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' => -- 1 Put_Line ("Good morning!"); when 'A' | 'a' => -- 2 Put_Line ("Good afternoon!"); when others => -- 3 Put_Line ("Please type 'm' or 'a'!"); end case; end Greetings;
Depending on the value of Answer, one of the three alternatives of the case statement will be executed. The vertical bar | can be read as meaning or so that choice 1 will be executed if the value of Answer is M or m and choice 2 will be executed if the value of Answer is A or a. Choice 3 (others) is executed if all else fails. The others choice must be the last one and is equivalent to the else part of an if statement. It is only executed if none of the other choices apply. A case statement must have a choice for every possible value of the controlling expression between case and is, so an others clause is usually necessary.
In situations where you want any one of a consecutive range of values to select a particular choice, you can specify a range of values in a case statement using .. to indicate the extent of the range. For example, if you wanted to test if Answer was a letter you could do it like this:
case Answer is when 'A' .. 'Z' | 'a' .. 'z' => Put_Line ("It's a letter!"); when others => Put_Line ("It's not a letter!"); end case;
This says that if the value of Answer is in the range A to Z or the range a to z, the message Its a letter! will be displayed. If it isnt, the message Its not a letter! will be displayed instead.
You can also test if a value is in a particular range using the operators in and not in. The case statement above could be rewritten as an if statement like this:
if Answer in 'A' .. 'Z' or Answer in 'a' .. 'z' then Put_Line ("It's a letter!"); else Put_Line ("It's not a letter!"); end if;
Not in is the opposite of in:
if Answer not in 'A' .. 'Z' then Put_Line ("It's not a capital letter!"); end if;
Case statements must cover all the possible values of the expression between case and is. This means that there has usually to be a when others clause, but sometimes you dont want to do anything if the value doesnt match any of the other selections. The solution is to use the null statement:
when others => null; -- do nothing
The null statement is provided for situations like this one where you have to say something but dont want to do anything. A null statement has no effect at all except to keep the compiler happy by telling it that you really do want to do nothing and that you havent just forgotten something by accident.
At the moment you only get one chance to answer the question that the program asks. It would be nicer if you were given more than one attempt. Here is a program that does that:
with Ada.Text_IO; use Ada.Text_IO; procedure Greetings is Answer : Character; begin loop -- 1 Put ("Is it morning (m) or afternoon (a)? "); Get (Answer); if Answer = 'm' or Answer = 'M' then Put_Line ("Good morning!"); exit; -- 2 elsif Answer = 'a' or Answer = 'A' then Put_Line ("Good afternoon!"); exit; -- 3 else Put_Line ("You must type m or a!"); end if; end loop; -- 4 end Greetings; -- 5
This program contains a loop statement which starts at line 1 and ends at line 4. Again, its a compound statement; it starts with loop on line 1 and ends with end loop and a semicolon on line 4. The sequence of statements it encloses will be repeated over and over again when its executed. It will only stop repeating when you execute one of the exit statements on lines 2 and 3. The exit statement terminates the loop and execution of the program continues at the point after end loop. In this case it is line 5, the end of the program.
Quite a lot of the time you want to exit from a loop when a particular condition becomes true. You could do this using an if statement:
if This_Is_True then exit; end if;
but its a common enough requirement that Ada provides a special form of the exit statement:
exit when This_Is_True;
Heres another way of writing the same program as before which illustrates the use of an exit when 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 -- 1 Put_Line ("Good morning!"); else Put_Line ("Good afternoon!"); end if; end Greetings;
The previous version displayed the message Good morning! or Good afternoon! from within the loop; here, the loop just checks whether the answer is valid. As soon as it is valid, the exit statement terminates the loop and execution continues at the next statement, namely the if statement at line 1 which is now responsible for displaying Good morning! or Good afternoon!. By the time we get to line 1, we know that Answer is either an m or an a in either upper or lower case and so the if statement only has to test if its an m or an M; if it isnt it must be an a or an A.
In many cases the exit statement is the first statement in a loop; you will often want to test that everythings all right before you do anything else:loop exit when End_Of_File; -- i.e. when there is no more input -- get some input and process itend loop;
This is a common enough situation that theres a special form of the loop statement to cater for it:
while not End_Of_File loop -- get some input and process it end loop;
The operator not inverts the sense of a condition; if a condition X is True then not X is False and vice versa. Note that the condition in a while loop tells you when to repeat the loop whereas the condition in an exit when statement tells you when to exit from it, so a loop that begins exit when X gets rewritten as while not X loop ....
Another common requirement is to repeat a loop a fixed number of times. This is a frequent enough situation that Ada provides another form of the loop statement to handle it (a for loop). Heres an example which displays a line of 20 asterisks:
for N in 1..20 loop Put ("*"); end loop;
The range 1..20 specifies how many times the loop will be executed. The control variable N will take on successive values from 1 to 20 each time around the loop (i.e. in this case, it will always give the number of times the loop has executed so far). N doesnt need to be declared elsewhere; it is automatically declared by its appearance in the loop heading. For loops are dealt with in more detail in chapter 6.
You are also allowed to name loops by attaching labels to them:
Main_Loop: loop ... end loop Main_Loop;
The label is a name followed by a colon immediately before the loop statement. Note that if you use a loop label, the name must be repeated after end loop as in the example above.
The main reason for providing a loop label is so that you can specify which loop an exit statement should exit from. This allows you to exit several loops at once:
Outer: loop Inner: loop ... exit Outer when Finished; -- 1 end loop Inner; end loop Outer; -- 2
The exit statement at line 1 will exit from both the inner and outer loops, and execution will continue at line 2 (after the end of the outer loop). This is something which is rarely required in practice; if it does seem to be necessary, its worth having a good think about your design since there are usually better ways to achieve the same effect.
Armed with all this extra knowledge about Ada we are now in a position to improve somewhat on the calculator program from the previous chapter. It can be rewritten to accept expressions like 123+456 or 325 and display the answer. This is simply a matter of reading an integer, a character and another integer, and then performing the appropriate operation depending on the character between the two integers:
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;
One problem with this is that the operator is taken to be the first character after the first integer, which doesnt allow for any separating spaces. The integers can be preceded by spaces because of the way that Get works for integers, so that 123+ 456 will be accepted but 123 + 456 wont be. The answer is to use a loop to skip spaces between the first integer and the operator:
Get (First); loop Get (Operator); exit when Operator /= ' '; end loop; Get (Second);
The operator /= means not equal to, so the exit statement will be executed when Operator is not equal to a space. This means that as long as it is a space, well go round the loop and get another character, thus ignoring all spaces.
An even better idea is to extend the program further so that it can read expressions involving more than one operator, e.g. 1+2+3. This will involve reading the first integer and making it the result, then reading successive pairs of operators and integers and adding them (or whatever) to the result. This will give a strictly left-to-right evaluation, so that 1+2*3 will come out as 9 rather than 7 as you might expect; in a later chapter I will show you how to deal with issues like doing multiplication and division before addition and subtraction. To simplify matters I will require the user to type in a full stop to terminate the expression.
This is a slightly larger program than the previous ones; rather than just showing you the complete program Im going to take you through a step-by-step design process. Reading and understanding the programs Ive shown you so far is important for getting to grips with the facilities that Ada provides, but at some point you have to start writing your own programs and for this you need some tips on how to get started. A lot of people find it easy to understand a program once its been written but find it hard to know where to start if they have to write it themselves.
In general you can break any programming problem down into three main components: some initialisation to get things ready at the beginning, followed by the main processing involved, followed by some finishing off at the end. In this case the initialisation will involve displaying a prompt and reading an arithmetic expression, the main processing will involve evaluating the expression that the user has typed in, and the finishing off at the end might just involve displaying the result. Well need an integer variable for the result to be displayed. This gives the following as a first stab at the program:
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Calculator is Result : Integer; begin Put ("Enter an expression: "); -- process the expression typed in by the user Put (Result, Width => 1); New_Line; end Calculator;
The next thing to do is to decide how to break down the main processing. A good way to start is to consider the structure of the input that the program will be expected to deal with. Here are some samples of the input Id expect this program to accept:
2. 2+2. 2+2*2. 2+2*2-2.
What we have here is an integer followed by any number (zero or more) of arithmetic operators each of which is followed by an integer, followed finally by a full stop. An old programming rule of thumb says that the structure of a program tends to reflect the structure of its input; in this case well need to read the first number, then repeatedly process operators and the numbers which follow them until we reach a full stop. So this gives us a sequence of two steps: read the first number and then process the rest of the expression. In the case where there are no operator/integer pairs after the first number, the result will be the first number. This leads to the conclusion that the first number should just be read into the variable Result:
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Calculator is Result : Integer; begin Put ("Enter an expression: "); Get (Result); -- repeatedly process operator/integer pairs (if any) Put (Result, Width => 1); New_Line; end Calculator;
Processing operator/integer pairs is a repetitive activity, so well need a loop statement. With any loop you need to ask yourself when the loop will terminate; in this case it is when the final full stop is read, or when something unexpected is read (which should be reported as an error). The result wont need to be displayed if an error occurs, so we can display it when the full stop is encountered, rather than at the end of the program as Ive got it above. This can be done by moving the call to Put into the loop and following it by an exit statement.
Inside the loop well need to read the next character, which should be either an operator or the terminating full stop, so well need a character variable (which Ill call Operator) to store it in:
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Calculator is Result : Integer; Operator : Character; begin Put ("Enter an expression: "); Get (Result); loop Get (Operator); if Operator = '.' then -- display result and exit from the loop Put (Result, Width => 1); exit; else -- process the rest of an operator/integer pair end if; end loop; -- the call to Put has now been moved into the loop New_Line; end Calculator;
As in the previous program well want to ignore spaces before the operator; to do that, Ill just use the code which I showed you earlier without any further comment:
with Ada.Text_IO, Ada.Integer_Text_IO; use Ada.Text_IO, Ada.Integer_Text_IO; procedure Calculator is Result : Integer; Operator : Character; 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 -- process the rest of an operator/integer pair end if; end loop; New_Line; end Calculator;
Processing the rest of the operator/integer pair involves reading the integer (which means we need another Integer variable) and then applying the operator to the result so far and the integer that weve just read:
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); -- apply the operator to Result and Operand end if; end loop; New_Line; end Calculator;
Applying the operator involves a choice between a number of alternatives: if its an addition operator we want to add the numbers together, if its a multiplication operator we want to multiply them, and so on. Since we have a choice between several alternatives we have to use either an if statement or a case statement. As you saw earlier, a case statement is a convenient solution in this case where all the choices depend on a particular value, in this case the value of Operator. The first number is in Result and the second is in Operand, so well need to evaluate Result+Operand, Result-Operand or whatever. This will give us a new result which needs to be stored in Result so that its ready to be displayed when we get to the end of the program, so we need each choice to be an assignment statement along the lines of:
Result := Result + Operand;
Note that the old value of Result is used on the right hand side of := to calculate the new value of Result. The right hand side of the assignment is evaluated by adding the old value of Result to Operand; this value is then stored in Result, replacing the old value. This idiom is commonly used to add 1 to the existing value of a variable, like this:
Result := Result + 1; -- add 1 to Result
A when others choice will be needed in the case statement to cope with the fact that Operator might be any Character value, not just one of the four operators were looking for. The good news is that the compiler would complain if we forgot this little detail. If we get to the when others choice it means theres an error in the input. An appropriate response to this is to display an error message and get out of the loop with an exit statement. So here at last is the final program:
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); -- 1 loop -- 2 loop -- 3 Get (Operator); exit when Operator /= ' '; end loop; if Operator = '.' then -- 4 Put (Result, Width => 1); -- 5 exit; -- 6 else Get (Operand); -- 7 case Operator is when '+' => Result := Result + Operand; -- 8 when '-' => Result := Result - Operand; when '*' => Result := Result * Operand; -- 9 when '/' => Result := Result / Operand; when others => Put ("Invalid operator '"); -- 10 Put (Operator); Put ("'"); exit; -- 11 end case; end if; end loop; New_Line; end Calculator;
If you type 1+2*3. in response to the prompt, what will happen is that line 1 will read the value 1 into Result. Line 2 is the start of the main loop; the first thing inside this loop is another loop (line 3) to skip over any spaces in front of the operator character. We will end up at line 4 with Operator holding the character '+'. Lines 5 and 6 will display the result and exit the main loop when Operator is a full stop, but we havent got to that stage yet. So line 7 will read the value 2 into Operand, and then the case statement will execute line 8 based on the value of Operator. Line 8 calculates the value Result+Operand (i.e. 1+2) and stores the result (i.e. 3) in Result. The upshot of this is that Result has been altered from 1 to 3.
After line 8 has been executed we go round the main loop a second time. Operator ends up holding the character '*' and Operand ends up holding the value 3. The case statement executes line 9, which multiplies Result (3) by Operand (also 3) to give a new value of 9 for Result. Around the loop again, and Operator ends up holding a full stop at line 4. The result (9) is then displayed by line 5 before exiting from the main loop at line 6.
If an invalid operator character is typed in (e.g. 1&2.) the section of the case statement at line 10 gets executed, which displays an error message. Line 11 then exits from the main loop. Notice how important it is to think about what can possibly go wrong and to deal with it in a sensible way; its easy to write a program that gives the right answer for valid input, but its much harder to write a program that can cope sensibly with bad input as well.
Of course, the program is still not completely bulletproof. If you type gibberish like XYZZY at the point where the program expects an integer, the program will halt with an error message. If youre unlucky it will just say something like unhandled exception; some compilers are more helpful, and will also tell you that the error was an exception called Data_Error, and possibly tell you which line of the program you were at when it happened. A Data_Error means that the input is in the wrong format; another common one is Constraint_Error, which youll get if you go outside the range of values allowed for Integer on your system (try 1000000*1000000*1000000, which will almost certainly be too big to handle).
A properly designed program should be able to cope with any input at all, not just correct input. To manage this we need to trap Constraint_Error and Data_Error exceptions and deal with them sensibly. Ada allows us to provide exception handlers to specify what happens if an exception occurs. This is a topic Ill return to in more detail later, but its worth a brief introduction before we go any further so that youll be able to start making the programs you write more robust.
You can put an exception handler into any block of statements enclosed by begin and end, e.g. a procedure body:
procedure X is begin -- your code goes here as usual exception when Some_Exception => Do_This; end X;
where Some_Exception is the name of an exception you want to handle and Do_This is the action you want to take. The action can be any sequence of statements; it can be a null statement which does nothing, which will have the effect of ignoring the exception, or it can be something more elaborate. In this case a sensible action might be to print out an error message when a Constraint_Error or a Data_Error occurs. Heres how to do it:
procedure Calculator is Result : Integer; Operator : Character; Operand : Integer; begin Put ("Enter an expression: "); ... code to process the expression as before exception when Constraint_Error => Put_Line ("Value out of range"); when Data_Error => Put_Line ("Error in input -- integer expected"); end Calculator;
The exception handler section goes at the very end; its ignored if there arent any errors. If a Constraint_Error or a Data_Error is reported (or raised, to use the correct terminology), you immediately end up at the appropriate exception handler and do what it says. Once youve done this, youre at the end of the procedure and the program terminates.
If you want the program to give the user another chance rather than terminating you need to be a bit more subtle. Heres how you can safely read a value into an Integer variable called X:
loop begin Put ("Enter an integer: "); -- 1 Get (X); -- 2 exit; -- 3 exception when Constraint_Error | Data_Error => Put_Line ("Error in input -- please try again."); -- 4 Skip_Line; -- 5 end; end loop; -- 6
Note that you cant put an exception handler directly between loop and end loop; you have to put begin and end around the section that you want to provide exception handling for, and then put the exception handler section immediately before end. This is the only case in Ada where end is not followed by something to say what it is the end of.
What happens here is that line 1 displays a prompt and line 2 attempts to read an integer. If an exception is raised by Get, you wont get to line 3; instead, youll be whisked off to line 4 which displays an error message. Line 5 calls a procedure Skip_Line from Ada.Text_IO to ignore the rest of the current line of input so the user will have to type another line. If you dont call Skip_Line after a Data_Error youll just end up reading the same bad data from the current line (which wont have been read since it wasnt valid).
After this youll be at line 6, the end of the loop, so youll go around and redisplay the prompt and get another line of input. When the user types in a valid value for X youll carry on past line 2 to line 3, which will exit from the loop.
Note also that you can handle several exceptions with a single handler by separating them by a vertical bar (|) in the same way as you would specify multiple choices in a case statement. Also as in a case statement, you can provide a catch-all handler by specifying when others:
exception when others => Do_Something; -- handle every exception the same way
As in a case statement, when others must come last if you have more than one exception handler. It handles any exceptions not dealt with by the other handlers. If you use it as the only handler it will deal with any exception that occurs. I dont recommend using when others unless you really need to; it might disguise any real errors in your program due to undiscovered bugs which would otherwise be reported as unhandled exceptions.
If you want an exception to be handled in different ways in different places, you need to enclose each such place in a begin ... end block. For example, if you have two assignment statements which could each raise a Constraint_Error:
A := A ** 2; -- might raise Constraint_Error B := B ** 2; -- might raise Constraint_Error
you could enclose each one in a separate block with its own handler like this:
begin A := A ** 2; -- might raise Constraint_Error exception when Constraint_Error => Put_Line ("Assignment to A failed"); end; begin B := B ** 2; -- might raise Constraint_Error exception when Constraint_Error => Put_Line ("Assignment to B failed"); end;
|3.1||Modify the Greetings program to say Good evening! in the evenings as well.|
|3.2||Modify the calculator program so that after evaluating an expression it asks the user if he or she wants to evaluate another expression. If the answer is y or Y (yes), evaluate another expression; if its n or N (no) exit from the program.|
|3.3||Write a program which asks the user to pick an animal from a list that you display (cat, dog, elephant or giraffe) and then asks Is it a household pet? to distinguish cats and dogs from elephants and giraffes. If the user says its a household pet, ask if it purrs; if not, ask if it has a long neck. Finally, tell the user which animal you think was chosen. Try extending the program to include a few more animals.|
|3.4||Write a program to count the number of vowels (A, E, I, O or U) in its input. Allow the user to type in a sequence of characters (as many as they like) ending with a full stop and then display the number of occurrences of each vowel as well as a grand total. This will involve using a set of integer variables which are set to zero at the start of the program. You will then need to add 1 to the appropriate variable whenever a vowel is typed in. Ignore case distinctions, so that a is treated as meaning the same as A.|
This file is part of Ada 95: The Craft
of Object-Oriented Programming by John English.
Copyright © John English 2000. All rights reserved.
Permission is given to redistribute this work for non-profit educational use only, provided that all the constituent files are distributed without change.
$Revision: 1.2 $
$Date: 2001/11/17 12:00:00 $