Previous | Next | Table of Contents | Index | Program List | Copyright

3.4 Data Structures: Introducing Enumeration Types

So far, most of the data types you have seen have been numerical (Integer, Float). In this section you will be introduced to the important concept of enumeration types. An enumeration type is defined by a list of values taking the form of identifiers. These types are called enumeration types because their values are enumerated, or given in a list. An enumeration type is useful in representing a fixed set of values that are not numerical, such as the days of the week, the months of the year, the years (freshman, sophomore, junior, senior) in a high school or college career, or the expenditure categories in an accounting program. Ada encourages you to use enumeration types by providing a small but useful set of operations on them and also an input/output package that makes it easy to read enumeration values from a keyboard or disk file and display them on the screen.

Defining Enumeration Types

In many programming situations, the standard data types and their values are inadequate. For example, in a budget program we might want to distinguish among the following categories of expenditures: entertainment, rent, utilities, food, clothing, automobile, insurance, and miscellaneous. We could always assign an arbitrary code that associates entertainment with a character value of 'e', rent with a character value of 'r', and so on. However, enumeration types allow us to specify the set of values directly. For example, the enumeration type Expenses declared below has eight possible values enclosed in parentheses:

    TYPE Expenses IS
        (entertainment, rent, utilities, food, 
        clothing, automobile, insurance, miscellaneous); 
     
    ExpenseKind : Expenses; 
    
The variable ExpenseKind (type Expenses) can contain any of the eight values listed after Expenses IS. The values, called enumeration literals, associated with an enumeration type are generally identifiers and therefore must conform to the syntax of identifiers. The type declaration must precede any variable declaration that references it.

The enumeration type Days has the values Monday, Tuesday, and so on:

    TYPE Days IS
        (Monday, Tuesday, Wednesday, Thursday,
        Friday, Saturday, Sunday);

SYNTAX DISPLAY
Enumeration Type Declaration

Form:
TYPE enumeration-type IS (identifier-list);

Example:
TYPE Class IS (Freshman, Sophomore, Junior,Senior); 

Interpretation:
A new data type named enumeration-type is declared. The enumeration literals, or values associated with this type, are specified in the identifier-list. The order in which the enumeration literals are given is important, because it defines an ordering of the literals: Freshman is less than Sophomore; Junior is greater than Freshman.

It is permissible for the same enumeration literal to appear in several enumeration types, just as it is permissible for the same numerical value to appear in several numerical types. It is, for example, possible to define the three types

    TYPE Traffic_Light_Colors IS
        (Red, Yellow, Green);
    TYPE Primary_Paint_Colors IS
        (Red, Yellow, Blue);
    TYPE Primary_TV_Colors IS
        (Red, Blue, Green);
in the same program without causing difficulties for the compiler.

Enumeration Type Attributes and Operations

The order relationship between the values of an enumeration type is fixed when the type is declared. Each literal has a position in the type, given as a value of type Natural. For type Days, the first value in its list (Monday) has position 0, the next value (Tuesday) has position 1, and so on.

An assignment statement can be used to define the value of a variable whose type is an enumeration type. The variable

    Today     : Days;  --current day of the week
    Tomorrow  : Days;  --day after Today 
specifies that Today and Tomorrow are type Days and, therefore, can be assigned any of the values listed in the declaration for type Day. Consequently, the assignment statements
    Today := Friday; 
    Tomorrow := Saturday; 
assign the values Friday to variable Today and Saturday to variable Tomorrow.

An important aspect of Ada's type system is the notion of attributes. These are characteristics of a type or variable that can be queried by a program. For the case of enumeration types, six important attributes are:

Some examples are given below; they assume that Today is Friday and Tomorrow is Saturday.
    Days'First is Monday 
    Days'Last is Sunday  
    Days'Pos(Monday) is 0  
    Days'Val(0) is Monday  
    Days'Pos(Sunday) is 6  
    Days'Val(6) is Sunday  
    Days'Pred(Wednesday) is Tuesday  
    Days'Pred(Today) is Thursday  
    Days'Succ(Tuesday) is Wednesday  
    Days'Succ(Today) is Saturday  

Because enumeration types are not cyclical (i.e., do not "wrap around"), the queries Days'Pred(Monday) and Day'Succ(Sunday) are undefined and would cause a run-time exception--namely, the raising of Constraint_Error--if attempted. Similarly, if Tomorrow had the value Sunday, Days'Succ(Tomorrow) would cause an exception. Whether the assignment statement

    Tomorrow := Days'Succ(Today); 
would cause an exception depends on the value of Today; it cannot cause a compilation error because the value of Today is usually unknown at compilation time.

In the next chapter you will see some examples of how to use careful checking of values in order to prevent exceptions from being raised.

SYNTAX DISPLAY
Attribute Query

Form:
type'attribute-name or type'attribute-name(value)

Example:
    Traffic_Light_Colors'First
    Days'Succ(Wednesday)
    Days'Pos(Today)
Interpretation:
An attribute query answers a question regarding certain characteristics of types or variables. For each type, the set of attributes is predefined by the language and cannot normally be changed by the programmer. Note the required presence of the single quote or apostrophe in the attribute query.

Input/Output Operations for Enumeration Types

One of the most convenient Ada features for using enumeration types is a built-in input/output package for reading and displaying enumeration literals. Within Ada.Text_IO is a generic package called Enumeration_IO, which cannot be used immediately. Instances must be created; each instance is "tailored" to read and display exactly the literals in a specific enumeration type. For example, in a program in which the type Days is defined and the variable declaration Today:Days appears, we could write

    PACKAGE Day_IO IS NEW Ada.Text_IO.Enumeration_IO(Enum=>Days);  
which would give us the ability to read a value from the keyboard into Today or to display the value of Today on the screen, using procedure calls like
    Day_IO.Get(Item => Today);  
    Day_IO.Put(Item => Today, Width => 10);   

In the case of Get, the exception Data_Error is raised if the value entered on the keyboard is not one of the seven literals in Days. In this manner the input/output system automatically checks the validity of the value entered, making sure that it is a legal value in the enumeration type.

SYNTAX DISPLAY
Enumeration Get Procedure

Form:
instance.Get (Item => variable );

Example:
Day_IO.Get (Item => Some_Day);  

Interpretation:
By instance we mean an instance of Enumeration_IO for some enumeration type. The next string of characters entered at the keyboard is read into variable (of the same enumeration type). Any leading blank characters or RETURNs are ignored. The first nonblank character must be a letter, and the characters must form an identifier. The data string is terminated when a nonidentifier character is entered or the space bar or RETURN key is pressed.

If the identifier read is not one of the literals in the enumeration type for which instance was created, Data_Error is raised.

SYNTAX DISPLAY
Enumeration Put Procedure

Form:
instance.Put (Item => variable , Width => field width, Set => Text_IO.Upper_Case or Text_IO.Lower_Case);

Example:
Day_IO.Put (Item => Some_Day, Width => 5, Set => Lower_Case);

Interpretation:
The value of variable (of some enumeration type) is displayed, using the next Width positions on the screen. If the value would occupy less than Width positions, it is followed by the appropriate number of blanks; if the value would occupy more than Width positions, the actual number of positions is used. If Width is omitted, a compiler-dependent width is used by default. The standard values Text_IO.Upper_Case and Text_IO.Lower_Case are used to determine the form of the displayed value. If Set is omitted, the value is displayed in uppercase.


Case Study: Translating from English to French Color Names

PROBLEM SPECIFICATION

Your roommate comes from France, and you are taking a watercolor-painting class together. You would like to have the computer give you some help in remembering the French names of the major colors, so that communication with your roommate will be easier. You'd like to enter an English color name on the keyboard and let the program display the corresponding French name. The English color names are white, black, red, purple, blue, green, yellow, and orange; the French color names are blanc, noir, rouge, pourpre, bleu, vert, jaune, and orange.

ANALYSIS

The French and English colors can be represented by two enumeration typesFrench_Colors and English_Colors and can be read and displayed using two instances of Enumeration_IO, which we will call French_Color_IO and English_Color_IO.

Data Requirements

Problem Data Types:

English colors, an enumeration type:

    TYPE English_Colors IS   
    	(white, black, red, purple, blue, green, yellow, orange);  

French colors, also an enumeration type:

    TYPE French_Colors IS   
	(blanc, noir, rouge, pourpre, bleu, vert, jaune, orange); 

Problem Inputs:

English color (Eng_Color : English_Colors).

Problem Outputs:

French color (Fr_Color : French_Colors).

DESIGN

We were careful to list the French and English colors in the same order, sogiven an English color, the corresponding French color will be in the same position in the French color type. The program depends on this correspondence, which gives us the following algorithm.

Initial Algorithm

1. Prompt the user to enter one of the eight English colors, Eng_Color.

2. Find the corresponding French color, Fr_Color.

3. Display the French color.

Algorithm Refinements

The only step needing refinement is step 2. We can find the French color corresponding to a given English one by using the Pos and Val attributes. Since the French and English colors have corresponding positions, we can find the position of the English color in its type, then use that position to find the corresponding value in the French type. To do this we shall use a program variable Position of type Natural to store the color position within its type.

Step 2 Refinement

2.1. Save in Position the position of Eng_Color in its type.

2.2. Save in Fr_Color the corresponding value in the French type.

TEST PLAN

This algorithm depends upon each of the French colors being in the same position in its type as the corresponding English color. Since the number of colors is relatively small, all the cases can be checked to be sure the two color types were given correctly. We also need to test for invalid input, for example, a word or other sequence of characters that is not an English color. If an invalid token is entered, Data_Error should be raised and the program should halt.

IMPLEMENTATION

The complete program is shown in Program 3.5. The program begins with a context clause for Text_IO. Within the program, the two color types are defined and instances of Enumeration_IO are created to read and display values of these types. Finally, the sequence of statements implements the refined algorithm just developed.

Program 3.5
Translating between French and English Colors

WITH Ada.Text_IO;
PROCEDURE Colors IS
------------------------------------------------------------------------
--| Displays a French color, given the corresponding English color
--| Author: Michael B. Feldman, The George Washington University 
--| Last Modified: July 1995                                     
------------------------------------------------------------------------
 
  TYPE English_Colors IS 
     (white, black, red, purple, blue, green, yellow, orange);
         
  TYPE French_Colors IS 
     (blanc, noir, rouge, pourpre, bleu, vert, jaune, orange);
           
  PACKAGE English_Color_IO IS 
     NEW Ada.Text_IO.Enumeration_IO (Enum => English_Colors);
 
  PACKAGE French_Color_IO IS 
     NEW Ada.Text_IO.Enumeration_IO (Enum => French_Colors);
 
  Eng_Color : English_Colors;
  Fr_Color  : French_Colors;
  Position  : Natural;
     
BEGIN -- Colors

  Ada.Text_IO.Put (Item => "Please enter an English color > ");
  English_Color_IO.Get (Item => Eng_Color);

  Position := English_Colors'Pos(Eng_Color);
  Fr_Color  := French_Colors'Val(Position);
   
  Ada.Text_IO.Put (Item => "The French color is ");
  French_Color_IO.Put (Item => Fr_Color, Set => Ada.Text_IO.Lower_Case);
  Ada.Text_IO.New_Line;

END Colors;
Sample Run
Please enter an English color > blue
The French color is bleu

TESTING

The sample run gives one test. To complete the test plan, run the other testsincluding one for invalid input.


Exercises for Section 3.4

Self-Check

  1. Evaluate each of the following assuming Today (type Day) is Thursday before each operation.

    a.  Day'Pos(Monday)         e.  Day'Succ(Sunday)
    b.  Day'Pos(Today)          f.  Day'Pred(Monday)
    c.  Day'Val(6)              g.  Day'Val(0)
    d.  Today < Tuesday         h.  Today >= Thursday
    
    


Previous | Next | Table of Contents | Index | Program List | Copyright

Copyright © 1996 by Addison-Wesley Publishing Company, Inc.