Dealing with errors and more work with decisions and loops
After completing this lesson you should be able to:
There are some exercises for you to do, each exercise has a sample answer:
Exercise 1 - using boundary values
Exercise 2 - using priming reads
Exercise 3 - using sentinel values and multiway
selection
When you have finished the lesson you might like to attempt these questions to assess how much you have learned.
Return to the index
Go to the next lesson
Return to the previous lesson
It is a fact of life in program design but there seems to be always one last bug or error to be corrected. We can broadly the classify the errors as:
The syntax errors aren't a serious issue during the program design phase since in practice, after designing and testing the design, the program will be implemented in a computer program language and it is at this point that syntax errors become a problem. Even so syntax errors are a minor problem since the process of building the program will capture the errors. The program simply won't build until all the syntax errors are removed. If you do some programming after this course you will get to understand the issue of syntax errors.
The logic errors are a much more serious problem since there is no way to eliminate these other than rigorously testing the program design.
The data errors are also serious errors and in some respects are harder to deal with than logic errors.
The theory and practice of testing program designs and programs is a large study within itself and this course doesn't provide a detailed view of program testing, there just isn't enough time. What we can do is develop a simple strategy for testing our designs and this should be enough for the present:
A strategy for testing program designs:
Much of the data we use comes from limited sets of data, for example:
For the set of positive integers a negative number or a real number like 98.675 are invalid data. For the set of letters of the alphabet the digits 1, 2, 3 etc are invalid data. We can say for instance that the boundaries of the set of letters are the letters 'A' and 'Z' or the lower case letters 'a' and 'z'. We can set boundaries in our programs:
DOWHILE (number > 0) AND (number < 101)
INPUT number
IF (number > 0) AND (number < 101)
THEN CALL Process_Number(Process_Number)
ELSE DISPLAY 'Error: invalid data was entered.'
ENDIF
END DOWHILE
Here we have set boundaries for the while loop and the call to Process_Number. The only integer values which will take part in processing are integers in the range 1 to 100 inclusive.
Examples like this don't tell the whole story. We can ensure that we don't process an integer value outside the boundaries but how do you protect this program from someone who enters a real number instead of an integer? For example the value 17.543 is in the range of 1 to 100 but it is not an integer. It is valid in the sense that it is inside the boundaries but invalid because it is the wrong type of data. This is quite a difficult issue to deal with and will be left for a later course but the principle is that the input to the program is screened on a character by character basis. In the case of entering positive integers into a program the only valid characters are the digits '0' to '9' and everything else, decimal points, negative signs, letters etc will be rejected. The sample above might be re-written as:
DOWHILE (number > 0) AND (number < 101)
CALL Input_Positive_Integer( number)
IF (number > 0) AND (number < 101)
THEN CALL Process_Number(number)
ELSE DISPLAY 'Error: invalid data was entered.'
ENDIF
END DOWHILE
You can see that there is a subprocess Input_Positive_Integer which will return a positive integer. How it does it isn't our business at the moment.
You need to pack some books in cardboard boxes. Design a program which measures the volume of boxes by accepting as input W (width), H (height), B (breadth). Since only positive integer values are permitted assume that you have a subprocess called Input_Positive_Integer and use it. You don't have to write the pseudocode for this subprocess. The permissible range of volumes is 100 volume units to 600 volume units inclusive. Any box outside this range is rejected. The books you are packing amount to 6000 volume units in total.
There is a sample answer here but attempt the exercise for yourself before looking at the sample.
A sentinel or guard value is a value which is used to terminate a loop. A typical use might be in the situation where a loop is used to accept values from the input device and a specific value indicates the end of data input. Here is an example:
price = 0
total = 0
DOWHILE price >= 0
DISPLAY 'Enter the value of the item sold. A negative amount will terminate input.'
INPUT price
total = total + price
END DOWHILE
DISPLAY total
In this example the sentinel value is any negative amount. If the user of the program enters a negative amount then the while loop terminates.
statement | price | total | price >= 0 |
1 price = 0 | 0 | ? | ? |
2 total = 0 | 0 | 0 | ? |
3 DOWHILE price >= 0 | 0 | 0 | TRUE |
4 INPUT price | 12.50 | 0 | TRUE |
5 total = total + price | 12.50 | 12.50 | TRUE |
3 DOWHILE price >= 0 | 12.50 | 12.50 | TRUE |
4 INPUT price | 22.00 | 12.50 | TRUE |
5 total = total + price | 22.00 | 34.50 | TRUE |
3 DOWHILE price >= 0 | 22.00 | 34.50 | TRUE |
4 INPUT price | -10.00 | 34.50 | TRUE |
5 total = total + price | -10.00 | 24.50 | TRUE |
3 DOWHILE price >= 0 | -10.00 | 24.50 | FALSE |
There is a problem isn't there? In this algorithm the sentinel value takes part in the processing of the total but the only purpose of the sentinel value is to terminate the loop. The result is that total is wrong. This is a logic error in the algorithm and the algorithm needs to be modified:
price = 0
total = 0
DOWHILE price >= 0
DISPLAY 'Enter the value of the item sold. A negative amount
will terminate input.'
INPUT price
IF price >= 0 THEN total = total + price
ENDIF
END DOWHILE
DISPLAY total
In the lesson on step-form design we had an example based on reading letters from the page of a book and determining if a letter was a vowel:
Step 2 assumed that somehow we knew what the number of letters on any given page was. In many practical situations we won't be so fortunate as to know how letters there are on each page but we almost certainly will be able to use sentinel values since each page will be separated by an end of page marker or a new page marker. A book stored on a computer will usually be stored page by page and each page is separated from the previous and next pages by some value we can look for. In practice this is a letter or character called FORMFEED. We could amend the algorithm to look for a FORMFEED:
count = 0
letter = ' '
DOWHILE letter <> FORMFEED
INPUT letter
IF letter is vowel THEN count = count + 1
ENDIF
END DOWHILE
You can see that this algorithm is quite different from the original. Apart from looking for FORMFEED I've also gotten rid of the counter which kept track of which letter on the page we were reading. I did this so that I could introduce the notion that each time a letter is read from the page the algorithm will automatically move on to the next letter position.
There is an animation here that tries to show how each read from the data advances one letter through the text.
There are situations where using sentinel values in while loops can get you into trouble. Here is an example of a typical trouble situation:
We have some data - a shopping list:
You can see I am very health conscious.
The sentinel value is END. The program calls the local grocer's computer via a modem and transmits the shopping list. It uses the pseudocode:
CALL connect_to_grocer
IF connect_OK
THEN CALL send_credit_card_number
DOWHILE shopping_item <> 'END'
INPUT shopping_item
CALL transmit(shopping_item)
END DOWHILE
CALL disconnect_link
ELSE DISPLAY 'Connection not available'
ENDIF
The grocer charges $5 for the use of the dial-in order system no matter whether you order a 100 items or 0 items. With the shopping list eggs,milk,fruit,bread,juice,yoghurt,END everything works OK but what if the shopping list is empty? That is, it just contains:
We send an empty shopping list to the grocer and he charges us $5 for the privilege. It would be better to only enter the while loop which actually sends the shopping list if there is a shopping list to send - we need to set up or prime the while loop. This means that we must establish the while loop condition - in this example shopping_item <> 'END' - before the while loop starts.
CALL connect_to_grocer
|
To prime the loop I have done two things:
The first sets the state of shopping_item <> 'END', the second ensures that the first INPUT isn't over-written in the event of a valid shopping list, and that the last thing done in the loop, apart from getting data, is the setting of the state of shopping_item <> 'END'. |
The dial-in shopping list algorithm has room for improvement. What problems does it have? Rewrite the algorithm to overcome the problems.
There is a sample answer here but try it for yourself before looking at the sample answer.
So far we have used simple single-level or two-way decision constructs but we can nest decisions within decisions to get multiple-level or multiway decision constructs:
IF number > 0
THEN IF number < 101
THEN CALL Process_Number(number)
ENDIF
ENDIF
This example terminates each IF ... THEN with ENDIF but this isn't absolutely necessary:
IF number > 0
THEN IF number < 101
THEN CALL Process_Number(number)
ENDIF
The general guide is that ENDIF is used where it is needed to clarify or emphasise where an IF ends. The next example uses IF ... THEN ... ELSE but only one ENDIF. My argument is that in this example the ENDIF for the inner decision isn't necessary since it is obvious where the inner decison ends.
IF number >= 1
THEN IF number <= 50
THEN DISPLAY 'In the range 1 to 50'
ELSE DISPLAY 'In the range 1 to greater than 50'
ELSE DISPLAY 'Number is negative'
ENDIF
If it was obvious where the inner decision ended in the previous example, what about this one?
IF number >= 1
THEN IF number <= 50
THEN DISPLAY 'In the range 1 to 50'
ELSE DISPLAY 'Number is negative'
ENDIF
Which IF does the ELSE belong to? The general rule is that an ELSE belongs to the last IF and in this example the pseudocode would look like this:
IF number >= 1
THEN IF number <= 50
THEN DISPLAY 'In the range 1 to 50'
ELSE DISPLAY 'Number is negative'
ENDIF
and this would be wrong, our program would be syntactically correct but logically wrong. We would have to include the ENDIF:
IF number >= 1
THEN IF number <= 50
THEN DISPLAY 'In the range 1 to 50'
ENDIF
ELSE DISPLAY 'Number is negative'
ENDIF
There is no limit to how deeply decisions can be nested:
IF condition1
THEN do_thing_1
ELSE IF condition2
THEN do_thing_2
ELSE IF condition3
THEN do_thing_3
ELSE IF condition4
THEN do_thing_4
ELSE do_thing_5
In this example we have a number of conditions (1 to 4) any one of which might be true. If condition1 is true then we execute the process do_thing_1 and the multiway selection is finished. If condition1 isn't true then we go on to evaluate the remaining conditions. If none of the conditions is true then we execute the subprocess do_thing_5.
Assume you have the following data stored somewhere:
Fred,Joan,Brian,Bert,Selie,Sue,Jack,Ng,Jacques,CLASS,Chris,Cheryl,Pam,Allan,CLASS,END
and it represents students in different classes. What might be the sentinel values in the data?
Design a program which:
There is a sample answer here but attempt the exercise for yourself before looking at the sample.
During this lesson you studied quite few topics, some dealing with errors and some which extended your knowledge of loops and decisions. You should have learned about:
Much of the material in this lesson should also extend to your knowledge of pseudocode and how to use it. This was the last lesson in the series on pseudocode, I used pseudocode for the vehicle for many different topics. All these topics though are applicable to the other methods of stating algorithms.
The next lesson introduces you flow charts the the first of the graphical techniques of program design.
This publication is copyright David Beech and Learning Systems 1997-2002
and may not be reproduced by any means without the written permission of
David Beech.
9 Wyndella Street, Tasmania, Australia