Dealing with errors and more work with decisions and loops

After completing this lesson you should be able to:

  1. list and describe the different kinds of errors that can occur in programs
  2. apply techniques which minimise the impact of errors
  3. describe what is meant by a sentinel value
  4. use sentinel values in while and repeat loops
  5. describe and use priming in while loops
  6. use multiway selection

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


Protecting programs from errors

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.

Testing program designs

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:

  1. Use a trace table to test program flow and the states of variables. This is quite a practical way to test small programs but can become impractical with large and complex programs.
  2. Try to test all data values. Again this is practical for small programs but not for programs which process large and diverse amounts of data. The best approach here is to test the data boundaries and we look at that next.

Error data and boundary conditions.

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.

Exercise 1:

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.

Terminating loops with sentinel values

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.

The trace table for the loop will be something like this:
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:

  1. Set curr to 1
  2. Set last to number of letters on the page
  3. Set count to 0
  4. Read letter at curr
  5. If letter is vowel then increment count
  6. Increment curr
  7. If curr <=  last go to step 4

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.

The while loop and priming reads

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.

Dial-in shopping list algorithm

CALL connect_to_grocer 
IF connect_OK 
  THEN CALL send_credit_card_number 
             INPUT shopping_item 
             DOWHILE shopping_item <> 'END' 
              CALL transmit(shopping_item) 
               INPUT shopping_item 
            END DOWHILE 
            CALL disconnect_link 
  ELSE DISPLAY 'Connection not available' 
ENDIF

To prime the loop I have done two things:

  1. a read from the shopping list before the loop starts
  2. moved the INPUT in the while loop to the end of the loop

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'.

Exercise 2:

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.

Multiway selection - nesting decisions

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.

Exercise 3

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:

  1. reads the data and displays the names of the students in the class
  2. counts the number of students in each class
  3. counts the number of classes

There is a sample answer here but attempt the exercise for yourself before looking at the sample.


Summary

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.



Return to the index
Go to the next lesson
Return to the previous lesson


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


db@codelearn.com