3.2 Simple Selection

A statement sequence requires the computer to step through a program precisely in the order in which the instructions in the original text file are written.

But, what if the solution demands one of the following:

1. That one or more steps be skipped if a variable has a certain value?

2. That one of two or more possible actions be taken depending on, say, what the user responds at the keyboard?

3. There be several alternative sections of code that could be executed depending, say, on the value of some variable?

The abstract solution mechanism presented in chapter one to handle such situations was called selection. Almost all computer languages implement actual selection statements that allow for changing the "flow of control" through the program in any of these situations.

Here are the general forms which your code can follow to handle each of the above three:

Situation 1

One or more steps should be skipped whenever a variable equals a particular value. Suppose a segment of code is calculating average marks, and is to print a congratulatory message for everyone who passes the course, but to print nothing otherwise. This could be diagrammed as in figure 3.2:

In Modula-2, the code might be written something like this:

  (*common code executed prior to entering selection section*)
  WriteString ("Your mark is");
  WriteReal (mark, 10);
  WriteLn;
  (*selection section*)
  IF mark > 50.0
    THEN
      WriteString ("congratulations, you passed the course");
      WriteLn;  (* semicolon optional before an end *)
    END;
  WriteString (nextMessage);
  WriteLn;
  (* now, carry on with rest of program *)

NOTES: 1. The reserved word IF must be accompanied by both the THEN and the END. These are also reserved words. The END of an IF is not the END of a named block, so it is not followed by a period.

2. Observe the positioning of semicolons and the way in which the statements are blocked out for easy readability. As mentioned above, the semicolon immediately before the END is optional.

Here is a section of code that with some modifications might appear in many types of programs. It arises whenever the user has been given a series of numbered choices (a menu) and then must respond. The code will repeat until a correct choice has been made.

choice := 8; (* a cardinal type, by the way *)
WHILE choice > 7
  DO
    WriteString (" enter your choice (0 - 7) here ==> ");
    ReadCard (choice);
    IF choice > 7
      THEN
        WriteString ("Your choice was invalid.  Try again.");
        WriteLn;
      END;  (* of the if *)
  END;  (* of the while *)

Situation 2

One of two or more possible actions is to be taken depending on, say, what the user responds at the keyboard. Suppose the two choices depend on whether the user responds with a Y for yes or an N for no. Figure 3.3 illustrates the desired flow of control.

Going beyond the specific illustration in figure 3.3, the idea behind this version of selection can be applied in a wide variety of circumstances.

Example 1:

This first example is a complete program designed to sort two input numbers into increasing order and then print them. It differs from the previous two in that two alternatives are present within the IF statement.

MODULE SortTwo;

FROM STextIO IMPORT
  WriteString, WriteLn, SkipLine;

FROM SWholeIO IMPORT
  ReadCard, WriteCard;

CONST
  fieldLength = 6;

VAR
  num1, num2 : CARDINAL;

BEGIN
  (* information *)
  WriteString ("This program sorts two cardinal numbers from smallest to largest.");
  WriteLn;
  (* collect the numbers from the user *)
  WriteString ("Enter the first number please. ==> ");
  ReadCard (num1);
  SkipLine;
  WriteLn;
  WriteString ("Enter the second number please. ==> ");
  ReadCard (num2);
  SkipLine;
  WriteLn;
  WriteString ("From least to greatest, the numbers are: ");
(* decide which way to write them and do it *)
IF num1 < num2
  THEN
    WriteCard (num1, fieldLength);
    WriteString (", ");     (* comma between *)
    WriteCard (num2, fieldLength);
  ELSE
    WriteCard (num2, fieldLength);
    WriteString (", ");
    WriteCard (num1, fieldLength)
  END; 	(* if *)

WriteLn;

END SortTwo.

Here is a run from this program with the user input marked in bold:

This program sorts two cardinal numbers from smallest to largest.
Enter the first number please. ==> 43
Enter the second number please. ==> 21
From least to greatest, the numbers are:     21,     43

NOTE: A new reserved word ELSE has been introduced to indicate an alternative action within the IF statement. One of the two alternatives will always be followed.

Example 2:

A tin can is shaped like a cylinder, and may or may not have a top lid. Write a program to compute the area and volume of the cylinder.

Discussion:

In order to save space, a complete plan for this program will not be provided. However, the following (very rough) pseudocode serves to indicate how to implement such alternate paths through the program.

  Print an introductory heading
  Print a prompt asking for the radius of the can
  Read the answer into the real variable radius
  Print a prompt asking for the height of the can
  Read the answer into the real variable height
  Compute the area of one lid using A = (pi)r^2
  Compute the area around the side using B = 2(pi)rh
  Compute the volume using V = Ah
  Set the initial total area to the side area
  Print a prompt asking if the cylinder has a top lid
  If the answer is Yes then
    total area = side area plus twice lid area
  Else if the answer is No then
    total area = side area plus lid area
  Print the results in an informative way

Here is the code:

MODULE CylVolArea;

(* Written by R.J. Sutcliffe *)
(* to illustrate IF..THEN..ELSIF *)
(* using P1 for the Macintosh computer *)
(* last revision 1993 02 11 *)

FROM STextIO IMPORT
  WriteString, WriteLn, SkipLine, ReadChar;

FROM SRealIO IMPORT
  ReadReal, WriteReal;
  
CONST
  pi = 3.14159265;  (* or, inport it from RealMath *)
  
VAR
  radius, height, lidArea, sideArea, totalArea, volume: REAL;
  answer : CHAR;

BEGIN 
  (* information *)
  WriteString ("CylVolArea was written by R.J. Sutcliffe");
  WriteLn;
  WriteString ("as an example of IF .. THEN .. ELSE");
  WriteLn;
  WriteLn;
  WriteString ("This code computes cylinder volumes and areas.");
  WriteLn;
  WriteLn;
	
(* Gather the information from the user *)
  WriteString ("Please enter the radius of the cylinder ==> ");
  ReadReal (radius);
  SkipLine;  (* consume the carriage return after the real *)
  WriteLn;
  WriteString ("Please enter the height of the cylinder ==> ");
  ReadReal (height);
  SkipLine;  (* consume the carriage return after the real *)
  WriteLn;
	
(* compute some results *)
  lidArea := pi * radius * radius;
  sideArea := 2.0 * pi * radius * height;
  totalArea := sideArea; (* initialize; more to be added later *)
  volume := lidArea * height;
	
(* Find out if cylinder has a top lid, and act accordingly *)
  WriteString ("Does this cylinder have a top lid? Y or N ==> ");
  ReadChar (answer);
  SkipLine;  (* skip to next line of input *)
  WriteLn;
	
(* start to print out answer *)
  WriteString ("For a cylinder with these dimensions ");
	
(* add appropriate lid area for one or two lids *)
  IF (answer = "Y") OR (answer = "y")
    THEN
      totalArea := totalArea + 2.0 * lidArea;
      WriteString ("and two lids ");
    ELSIF (answer = "N") OR (answer = "n") THEN
      totalArea := totalArea + lidArea;
      WriteString ("and one lid ");
    END;
  WriteString ("the volume is ");
  WriteReal (volume, 0);
  WriteLn;
  WriteString (" cubic units, and the area is ");
  WriteReal (totalArea, 0);
  WriteString (" square units.");
  WriteLn;
  WriteLn;
	
(* pause and wait for user to get a good look *)
  WriteString ( "Press any key to continue");
  ReadChar (answer);
  
END CylVolArea.

When this program was run twice with similar input (shown in bold) the following results were obtained:

Run#1

CylVolArea was written by R.J. Sutcliffe
as an example of IF .. THEN .. ELSE

This code computes cylinder volumes and areas.

Please enter the radius of the cylinder ==> 10
Please enter the height of the cylinder ==> 20
Does this cylinder have a top lid? Y or N ==> Y
For a cylinder with these dimensions and two lids the volume is  6.2831855E+3
 cubic units, and the area is  1.8849556E+3 square units.

Press any key to continue

Run#2

CylVolArea was written by R.J. Sutcliffe
as an example of IF .. THEN .. ELSE

This code computes cylinder volumes and areas.

Please enter the radius of the cylinder ==> 10
Please enter the height of the cylinder ==> 20
Does this cylinder have a top lid? Y or N ==> N
For a cylinder with these dimensions and one lid the volume is  6.2831855E+3
 cubic units, and the area is  1.5707964E+3 square units.

Press any key to continue

NOTES: 1. This code introduces two more reserved words, namely OR and ELSIF. Some other computing languages use ELSE IF (two words) and may do so in a slightly different way.

2. The logic here is as follows. If the user responds with a "Y" (uppercase or lowercase--they are different characters) then the area is calculated with two lids. If the response is "N" then the area is calculated with one lid, and if it is neither then the control of the program passes beyond the END of the IF with none of the four statements under the control of the IF being executed.

3. The last section (waiting for a keypress) may be necessary in some systems if their standard input/output erases the screen as soon as the program is finished.

The effect of this code is to interpret any answer other than "Y," "y," "N," or "n" as meaning that there are no lids at all. As this may be a rather undesirable result, the code could be rewritten as:

  IF (answer = "Y") OR (answer = "y")
    THEN
      totalArea := totalArea + 2.0 * lidArea;
      WriteString ("and two lids ");
    ELSE  (* assume a no answer if not yes *)
      totalArea := totalArea + lidArea;
      WriteString ("and one lid ");
    END;
If all values of an answer other than one particular one cause the same action, then that action is called the default.

In this case, the code has been modified to make "No" the default answer. If it is desired to check the answer and allow only one of the four specified ones to have any effect, the IF statement can be surrounded by a WHILE loop in the following manner:

answer := "X"; (* initialize it to none of the four *)
WHILE (answer # "Y") AND (answer # "y")
             AND (answer # "N") AND (answer # "n")
  DO
    ReadChar (answer);
    IF (answer = "Y") OR (answer = "y")
      THEN
        totalArea := totalArea + 2.0 * lidArea;
        WriteString ("and two lids ");
      ELSIF (answer = "N") OR (answer = "n") THEN
        totalArea := totalArea + lidArea;
        WriteString ("and one lid ");
      ELSE
        WriteString ("Invalid answer; please answer Y or N ");
        WriteLn;
      END; (* if *)
  END; (* while *)

If this is done, the code will cycle endlessly through the WHILE loop until the user does indeed respond with one of the required answers.

NOTE: The new reserved word AND has been introduced here.

Situation 3

There are several alternative sections of code that could be executed depending on the value of some variable. In this case, the code may have several ELSIF sections, and may or may not have an ELSE. What follows is a general framework for the IF..THEN..ELSIF..ELSE..END construction that can be used as a model for a variety of code.

  IF variable = value1
    THEN
      Statement Sequence1;
    ELSIF variable = value2 THEN
      Statement Sequence2;
    ELSIF variable = value3 THEN
      Statement Sequence3;
  ...

    ELSIF variable = valueN THEN
      Statement SequenceN;
    ELSE
      Default Statement Sequence;

    END;

Example:

The Aldergrove Credit Union super saver plan pays interest to its depositors at the prime rate provided they have over $20 000 on deposit, half a percentage point less if over $15 000, another half a percent less if over $12 000, with similar steps down at $10 000, $8 000, $6 000 and each $1000 thereafter. Write a program to compute the interest payable on any amount input by the user.

Discussion:

This program is a straightforward application of a multiple part IF statement, and is presented without any planning documents. Note that the minimum rate is achieved below $1000, that is, after eleven reductions amounting to 5.5%. Between this point and the $5000 threshold, the rate goes up in regular steps; after that in irregular ones.

MODULE SuperSaverInterest;

(* Written by R.J. Sutcliffe *)
(* to illustrate IF..THEN..ELSIF..ELSE *)
(* using Metropolis Modula-2 for the Macintosh computer *)
(* last revision 1993 02 11 *)

FROM STextIO IMPORT
  WriteString, WriteLn, ReadChar, SkipLine;

FROM SRealIO IMPORT
  ReadReal, WriteFixed;
    
VAR
  deposit, basicRate, actualRate, interest: REAL;
  answer : CHAR;

BEGIN
  WriteString ("Program written by R.J. Sutcliffe");
  WriteLn;
  WriteString ("as an example of IF .. THEN .. ELSIF .. ELSE");
  WriteLn;
  WriteLn;
  WriteString ("This program computes interest on deposits.");
  WriteLn;

(* Gather the information from the user *)
  WriteString ("What is the principal amount on deposit? ==> ");
  ReadReal (deposit);
  SkipLine;  (* reset to start of next line *)
  WriteLn;
  WriteString ("What is the prime interest rate? ");
  WriteString ("Please enter in decimal form. (6% = 0.06) ==> ");
  ReadReal (basicRate);
  SkipLine;   (* reset to start of next line *)
  WriteLn;

(* Now compute the actual rate from the basic rate and deposit. *)
  IF deposit > 20000.0
    THEN
      actualRate := basicRate
    ELSIF deposit > 15000.00 THEN
      actualRate := basicRate - 0.005
    ELSIF deposit > 12000.00 THEN
      actualRate := basicRate - 0.01
    ELSIF deposit > 10000.00 THEN
      actualRate := basicRate - 0.015
    ELSIF deposit > 8000.00 THEN
      actualRate := basicRate - 0.02
    ELSIF deposit > 6000.00 THEN
      actualRate := basicRate - 0.025
    ELSE
      actualRate := basicRate - 0.025 - 0.005 * FLOAT (TRUNC (deposit) DIV 1000);
    END;

  (* Take into consideration a really low prime that might otherwise produce a negative interest *)
  IF actualRate < 0.0
    THEN
      actualRate := 0.0
    END;

(* make the computation of interest and print the result *)
  interest :=  deposit * actualRate;
  WriteString ("The annual interest on $");
  WriteFixed (deposit, 2, 0);
  WriteString (" at ");
  WriteFixed (100.0 * actualRate, 2, 0);
  WriteString ("% is $");
  WriteFixed (interest, 2, 0);
  WriteLn;
  WriteLn;

  WriteString ( "Press any key to continue");
  ReadChar (answer);

END SuperSaverInterest.

Once again, notice the use of semicolons. As usual, placing them before such reserved words as END, ELSE, and ELSIF is optional.

When this program was run twice with similar input (shown in bold) the following results were obtained:

Run#1

Program written by R.J. Sutcliffe
as an example of IF .. THEN .. ELSIF .. ELSE

This program computes interest on deposits.
What is the principal amount on deposit? ==> 1000.00
What is the prime interest rate? Please enter in decimal form. (6% = 0.06) ==> 0.03
The annual interest on $ 1000.00 at  0.00% is $ 0.00

Press any key to continue

Run#2

Program written by R.J. Sutcliffe
as an example of IF .. THEN .. ELSIF .. ELSE

This program computes interest on deposits.
What is the principal amount on deposit? ==> 10000.00
What is the prime interest rate? Please enter in decimal form. (6% = 0.06) ==> 0.08
The annual interest on $ 10000.00 at  6.00% is $ 600.00

Press any key to continue

All the forms of the IF statement have now been used in the examples of this section. The complete syntax diagram is given in figure 3.4:


Contents