• No results found

Controlling the flow of your program

5.6 The CASEconstruct

<

In some situations it is necessary to have an ordering built in to the decision as to which choice to take because there is an overlap between 'some of the possible decision criteria. For example, if you are a baseball addict, but especially a Cubs fan, then the decision as to what to do 'on a Saturday afternoon might look like

this: ' ,~

If it is the baseball season and the Cubs are at home then

Go to Wrigley Field ;;,

Elseif it is the baseball season and the Cubs game' is on TV then Get a six-pack and watch the game on TV ",'

Elseif it is the baseball season then Go to any nearby baseball game Else

Rent a baseball video and watch it at home.

It is very clear that the order in which the choices are considered is of vital

importance! • ,

152 Controlling the flow of your program

Frequently, however, the decision criteria are mutually exclusive, and there is no overlap between them. For example, if you are a Liverpool fan, and are only interested in watching football matches in which they are playing (whether at home or away) then your Saturday afternoon decision plan might be rather different:

If it is the football season and Liverpool are playing at home then Go to Anfield and support the Reds

Else if it is the football season and Liverpool are playing away then Go to wherever they are playing and support the Reds Else

Get a six-pack and watch some of your old Liverpool videos at home.

Although this has been written in the same way as the previous example, there is clearly no ordering involved in this decision process and the use of if ... else style is rather misleading. An alternative approach would be to write

Select the appropriate case from the following alternatives:

Case 1:It is the football season and Liverpool are playing at home Go to Anfield and support the Reds

Case 2: It is the football season and Liverpool are playing away Go to wherever they are playing and support the Reds Case 3: Any other situation

Get a six-pack and watch some of your old Liverpool videos at home.

As well as the block IF construct, which caters for the ordered choice situation, Fortran 90 provides another form of selection, known as the CASE construct, to deal with the alternative situation in which the various alternatives are mutually exclusive, and the order in which they are expressed is unimportant.

Its overall structure is shown in Figure 5.12.

Both the block IF and the CASE constructs provide a means of selecting one from a set of blocks of statements and executing that block, or of executing

SELECT CASE (case expression)

CASE (case selector) block of Fortran statements

CASE (case selector) block of Fortran statements

END SELECT

Figure 5.12 The CASE structure.

The CASE construct 153 none of them if none of the decision criteria is satisfied. As we have already

mentioned, one difference between the two constructs is that in the CASE

construct the decision criteria must not overlap. The other major difference is that the expression which determines the selection must be a logical expression in a block IFconstruct, but may be an integer expression, a character expression or a logical expression in aCASE construct. This means that, in many situations, a more natural form of defining the different cases can be used than is possible with any form of IF construct.

The initial statement of aCASE construct takes the form

SELECT CASE (case_expression)

where, as already indicated, case_expression is either an integer expression, a character expression or a logical expression; real expressions are prohibited for this purpose. When the SELECT CASE statement is encountered the value of case_expression is evaluated and the block of statements which follow the appropriate CASE statement (if any) is executed.

Each CASE statement takes the form

CASE (case_selector)

or

CASE DEFAULT

although there may only be one CASE DEFAULT statement in a CASE construct.

The case_selector determines which, if any, of the blocks of statements will be obeyed, while the CASE DEFAULT statement, if any, precedes the block of statements to be obeyed if none of the other CASE statements produces a match.

The case_selector can take one of four forms:

case value low_value:

:high_value

low_value: high_value

or it may be a list of any combination of these. Note, however, that only the first form is permitted for logical values since it would be meaningless to list more than one of the possible two values. The meaning of these four alternatives is almost self-evident, but we shall elaborate them for the avoidance of any doubt:

(I) If the case_selector takes the form case_value then the following block of code is executed if and only if case_expression == case_value, where case_expression is an integer expression or a character expression, and if and only if case_expression .EQV. case_value, where it is a logical expression.

154 Contrail ing the flow of your program

(2) If the case_selector takes the form low_value: then the following block of code is executed if and only if low_value <= case_expression.

(3) If the case_selector takes the form : high_value then the following block of code is executed if and only if case_expression

<=

high_value.

(4) If the case_selector takes the form low_value: high_value then the following block of code is executed if and only if low_value <= case_expression .AND.

case_expression

<=

high_value.

If none of the specified values or value ranges matches the value of the case_expression then the block of code following the CASE DEFAULT statement, if any, is executed; if there is no CASE DEFAULT statement then an exit is made from the CASE construct without any code being executed.

Notice that the order in which the various CASE statements, and their following blocks of statements, are written does not matter, since the rules governing CASE statements require that there is no overlap. However, we recommend that, for clarity, any CASE DEFAULT statement be placed either as the firstCASE statement, or as the last, even though this is not necessary as far as the syntax is concerned. The choice as to which is preferable depends upon whether the CASE DEFAULT statement is expected to be the most normal selection, with the specified cases being exceptions, or whether it is a 'catch-all' to deal with those cases which are sufficiently rare not to justify individual treatment.

[] Problem

Read a date in the international standard form (yyyy-mm-dd) and print a message to indicate whether on this date in Sydney, Australia, it will be winter, spring, summer or autumn. For the purpose of this exercise we shall assume that winter consists of June and July, that spring is August, September and October, that summer is from November until March, and that the autumn is April and May.

~ Analysis

There are clearly four mutually exclusive cases, depending upon the value of the character string mm, and so the problem is ideally suited for a CASE statement.

Although it might be reasonable to assume that the date will be a valid one, this is, in general, a dangerous assumption and we should always check that data is valid. In this example aCASE DEFAULT statement can easily be used to identify any invalid data. Our data design and structure plan will be as follows:

The CASE construct 155 Data design

Purpose

Date (yyyy-mrn-dd) Month (for CASE)

Structure plan

Type

CHARACTER' 10 CHARACTER'2

Name date month

[II

Solution

PROGRAM seasons IMPLICIT NONE

A program to calculate in which season a specified date lies

!Variable declarations CHARACTER (LEN=10) :: date CHARACTER (LEN=2) :: month

! Read date

PRINT *,"P1ease type a date in the form yyyy-mm-dd"

READ * ,date

! Extract month number month = date(6:7)

! Print season SELECT CASE (month) CASE ("08":"10")

PRINT * ,date," is in the spring"

CASE ("11","12","01":"03")

PRINT *,date," is in the summer"

156 Cantrall ing the flow of your program CASE ("04", "05")

PRINT *,date," is in the autumn"

CASE ("06","07")

PRINT *,date," is in the winter"

CASE DEFAULT

PRINT *,date," is not a valid date"

END SELECT END PROGRAM seasons

Note that, because the case selector is a character expression, the case values must be expressed as character constants. Furthermore, ~e have assumed that there are no possible two-character strings which will lie between "08" and

"ro"

other than "09", or between "01" and "03" other than "02". No character coding system known to the authors fails to encode the ten digits 0 to 9 in successive places, but there is no formal requirement to do so. An alternative would be to write the selectors for the spring and summer as

CASE ("08", "09", "10") and

CASE ("11", "12", "01", "02", "03")

respectively.

Another alternative would be to convert the month to integer form, but since this is slightly awkward it would be difficult to justify in such a simple program, and we shall leave it as an exercise for the adventurous reader.

Finally, it is not necessary to use the variable month at all. The SELECT CASE statement could equally well have been written as

SELECT CASE (date(6:7))

although it is then marginally less clear what is going on.

[I]

Problem

Write a program to read the coefficients of a quadratic equation and print its roots.

[!]

Analysis

This program will use the formula

The CASE construct 157

-b:!: J(b2 - 4ac)

x=---2a

where

a~

+

bx

+

c=0 and a

-I

0

It is immediately apparent that there are three possible cases:

(1) b2

>

4ac

in which case the equation will have two real roots (2) b2 = 4ac

in which case the equation will have one root (or two coincident roots) (3) b2

<

4ac

in which case the equation will have no roots (or at least no real roots, and we are not concerned with imaginary roots in this example)

At first sight, since there are three mutually exclusive cases, this seems a natural problem in which to use aCASE statement. However, there are two major difficulties.

The first of these is that the values of the coefficients in this sort of problem will normally be real, and therefore the expression b2

<

4ac will also be real. The case_expression in aCASE statement must, however, be integer, character or logical.

The other problem concerns case 2, where the value of the expression is zero. We have stressed on many occasions that real arithmetic is only an approximation. In particular we should never compare two real numbers for equality, as two numbers which are mathematically equal will often differ very slightly if they have been calculated in a different way. We avoid this difficulty by comparing the difference between two real numbers with a very small number.

Thus we could rewrite the second case as follows:

(2)

I

b2 - 4ac

I <

epsilon

where epsilon is a very small number, in which case the equation will have one root

If we wish to use aCASE statement then we could deal with both of these problems at the same time by dividing the value ofb2 - 4ac by epsilon and then assigning the result to an integer for use in theCASEstatement. This will mean that if

I

b2 - 4ac

I <

epsilon the result of the division will be between -1 and

+

1,and

the integer stored will, as a result of truncation, be zero. Ifb2 - 4ac

>

epsilon then the result stored will be a positive integer, while if b2 - 4ac

<

epsilon the result stored will be a negative integer. Notice, however, that there is a further problem in choosing the value of epsilon arising from the fact that we shall be dividing b2 - 4ac by this very small number; it is always possible that dividing by a very

158 Controlling the flow of your program

small number might lead to a result which is larger than the largest number that can be stored on the computer! This indicates that the approach that we have chosen is not a particularly good one and should not be used in a real programming situation;

it will, however, suffice for this example of how to use CASE statements.

Note that this analysis has also ignored two other theoretical difficulties.

The first of these is the situation if a = O. In this situation the equation is not a quadratic equation and so for this example, in which the coefficients are being typed at the keyboard, we shall simply assume that a non-zero value will be typed fora;it would not be difficult to test for this case and return an appropriate value for x. It does not, of course, matter ifbor c is zero, since the equation will still be a quadratic.

The second problem is that the calculation ofb2and that of4accould lead to problems ifa, bor c is so large that the resulting calculation leads to a value greater than the maximum capable of being stored on the computer system being used (a condition known as overflow). Again, since the coefficients are being typed at the keyboard we shall assume that they are 'reasonable' numbers, and will ignore this problem here. Both of these situations should be considered in a comprehensive solution to this, apparently simple, problem. They are discussed in more detail in Chapter 18.

We can now design our program:

Data design

Purpose Type Name

A Local constant:

A small value REAL epsilon

B Local variables:

Coefficients REAL a,b,c

Intermediate value REAL d CASE selection value INTEGER selector

Structure plan

The CASE construct 159

We may note here that, since all possible cases have been covered, any one of these cases could be treated as the default case. However, for clarity it is preferable, in this example, to specify all three conditions explicitly, with the result that no default case need be specified.

Before writing the actual program we shall note that, despite the fact that the problem appears to be suitable for a CASE statement, the awkwardness in calculating a suitable value for use as a case selector might make the use of a block IF construct more appropriate. In this case a suitable design would be:

Data design

Purpose A Local constant:

A small value B Local variables:

Coefficients Intermediate values

Structure plan

Type

REAL 'REAL REAL

Name

epsilon

In this situation, the order in which the tests are carried out does matter.

First we test whether b2 - 4ac is greater than or equal, to epsilon, since this is anticipated to be the most usual case. If it is not then it is zero (for our purpose) or negative. We now test whether it is greater than a very small negative value (epsilon). If it is, then, since it is also less than a very small positive value, it can be considered to be zero. If neither of these cases holds then there can be no roots.

Which of these two approaches is used is largely a matter of personal style.

However, in most cases it will be quite clear which approach is to be preferred.

We shall write programs in bo~h the ways planned above.

@]

Solution

(a) Using a CASE construct PROGRAM quadratic_by_CASE

IMPLICIT NONE

160 Controlling the flow of your program

A program to solve a quadratic equation using a CASE statement to distinguish between the three cases

! Constant declaration

PRINT *,"Please type the three coefficients a, band c"

READ *,a,b,c

! Calculate b**2-4*a*c and resulting case selector d = b**2 - 4.0*a*c

selector = d/epsilon

! Calculate and print roots, if any SELECT CASE (selector)

PRINT *," The equation has two roots: ",xl," and ",x2 CASE (0)

! One root xl = -b/(a+a)

PRINT *,"The equation has one root: ",xl CASE (:-1)

! No roots

PRINT *,"The equation has no real roots"

END SELECT

(b) Using an IF construct PROGRAM quadratic_by_block_IF

IMPLICIT NONE

! A program to solve a quadratic equation using a block IF

! statement to distinguish between the three cases

! Constant declarations

REAL, PARAMETER :: epsilon=lE-6

! Variable declarations REAL:: a,b,c,d,sqrt_d,x1,x2

! Read coefficients

PRINT *,"Please type the three coefficients a, band c"

READ *,a,b,c

Obsolete forms of control statements 161

! Calculate b**2-4*a*c d = b**2 - 4.0*a*c

! Calculate and print roots, if any IF (d>=epsilon) THEN

! Two roots sqrt_d = SQRT(d) xl = (-btsqrt_d)/(ata) x2 = (-b-sqrt_d)/(ata)

PRINT *, "The equation has two roots: ",xl," and" ,x2 ELSE IF (d>-epsilon) THEN

! One root xl = -b/(ata)

PRINT *,"The equation has one root: ",xl ELSE

! No roots

PRINT *,"The equation has no real roots"

END IF