Monday, August 13, 2012

AdaTutor - Simple Declarations and Simple Attributes (2)


A subtype does not define a new type, but it imposes a range constraint on objects of that subtype.  For example,

subtype Day_Subtype is Integer range 1 .. 31;
D : Day_Subtype; -- D can have only Integer values from 1 to 31.

type Rainbow_Color is (Red, Orange, Yellow, Green, Blue, Indigo,
subtype Bright_Color is Rainbow_Color Range Orange .. Green;
B : Bright_Color; -- B can have only Rainbow_Color values from
                  -- Orange to Green.

subtype Probability is Float range 0.0 .. 1.0;
P : Probability; -- P can have only Float values from 0.0 to 1.0.

Every time the program stores a value into D, B, or P, a check is made to see if the value is in range.  If it isn't, the message Constraint_Error is displayed.  Constraint Errors are usually detected at run time.

Since subtypes don't define new types, the type of D is Integer, the type of B is Rainbow_Color, and that of P is Float.  Thus, if we write I : Integer; we may write D := D + I; etc., because D and I have the same type.

We don't have to supply a name for the subtype explicitly.  If we declare

    D : Integer range 1 .. 31;
the compiler creates its own name for the subtype internally; we never see it.  This is called an anonymous subtype.  The name the compiler creates will be one that we can't accidentally create ourselves.  Perhaps it will contain a control character or punctuation mark that Ada doesn't allow us to put into an identifier.  The above declaration is equivalent to
   subtype (something) is Integer range 1 .. 31;
   D : (something);
where (something) represents the anonymous subtype that we can't see or write.

Simple Attributes

An attribute consists of an apostrophe (called a "tic" mark for short), and the name of the attribute.  Attributes often follow type names.  They're something like functions, except that they usually involve a type.  For example:
 type Rainbow_Color is (Red, Orange, Yellow, Green, Blue, Indigo,
 type Traffic_Light_Color is (Red, Amber, Green);
 Rainbow_Color'Succ(Green)       is Blue
 Traffic_Light_Color'Pred(Green) is Amber

The attribute 'Succ stands for successor, and 'Pred stands for predecessor.  In Rainbow_Colors, the successor of Green is Blue, and in Traffic_Light_Colors, the predecessor of Green is Amber.  Thus we could write R : Rainbow_Color; and then R := Rainbow_Color'Succ(Green);.  You'll get a Constraint_Error if you try to take the successor of the last value or the predecessor of the first; for example, taking Rainbow_Color'Pred(Red) will cause an error.

'Succ and 'Pred work with any scalar type.  (In Ada 83, they work only with discrete types, meaning any integer or enumeration type.)  These two attributes aren't particularly useful with integer types, because we can simply add or subtract 1 instead.  So they're used most often with enumeration types.

The attribute 'Pos converts from a discrete type to an integer, and 'Val converts from an integer to a discrete type.  Again, the type is usually an enumeration type; there's little point converting from integer to integer.

For example, Rainbow_Color'Pos(Orange) is 1.  (The positions are numbered from zero.)  Rainbow_Color'Pos(Red) is 0 and Rainbow_Color'Pos(Violet) is 6Traffic_Light_Color'Pos(Green) is 2, but Rainbow_Color'Pos(Green) is 3Character'Pos('A') is 65, because the ASCII value of 'A' is 65, and the first 128 values of the Ada type Character contains the ASCII characters in order.  Character'Pos('0') is 48, because the ASCII value of the character '0' is 48.

'Val converts the other way, so Rainbow_Color'Val(0) is Red, and Rainbow_Color'Val(6) is Violet.  Taking the Rainbow_Color'Val of a parameter outside the range 0 .. 6 will raise a Constraint_Error.  Character'Val(65) is 'A', and Character'Val(7) is the "bell" character (control-G).  Since Boolean is an enumeration type, Boolean'Val(0) is False and Boolean'Val(1) is True.


type Month_Type is (Jan, Feb, Mar, Apr, May, Jun,
                    Jul, Aug, Sep, Oct, Nov, Dec);
What is Month_Type'Pos(Feb)?
  1. Month_Type'Pos(Feb) is 1.
  2. Month_Type'Pos(Feb) is 2.

While 'Pos and 'Val convert to and from integers, the attributes 'Image and 'Value convert to and from Strings.  They work with any scalar types (in Ada 83, only with discrete types), and are useful with integer types as well as enumeration types.  For example,

   type Rainbow_Color is (Red, Orange, Yellow, Green, Blue, Indigo,
   type Traffic_Light_Color is (Red, Amber, Green);

   Rainbow_Color'Value("RED")       is Red
   Rainbow_Color'Value("yellow")    is Yellow
   Traffic_Light_Color'Image(Amber) is "AMBER"

   Integer'Value("123") is  123
   Integer'Image(123)   is " 123"
   Integer'Image(-123)  is "-123"

If I is an Integer, we can write Put(Integer'Image(I)); without instantiating Integer_IO or making use of Ada.Integer_Text_IO.  However, in Ada 83 this won't work for type Float, only for discrete types.  'Value will raise a Constraint_Error if the String can't be converted to the discrete type.  For example, taking Integer'Value("12X3") or Rainbow_Color'Value("CHARTREUSE") will normally display Constraint_Error on the screen and stop the program.

For any scalar type, the attributes 'First and 'Last are also available.

 Rainbow_Color'First is Red and
 Traffic_Light_Color'Last is Green

A program can use these attributes with Integer to determine the size of integers on the host computer.  For example, if your PC Ada has 16-bit 2's complement integers, while a mainframe Ada uses 32-bit 2's complement, then

Integer'First is -32_768 on your PC and -2_147_483_648
 on the mainframe.
Integer'Last  is  32_767 on your PC and 2_147_483_647
 on the mainframe.

Most attributes may also be used with subtypes:

subtype Day_Subtype is Integer range 1 .. 31;
Day_Subtype'First is 1 and Day_Subtype'Last is 31

There are two subtype definitions involving 'Last built into the Ada language:

    subtype Positive is Integer range 1 .. Integer'Last;
    subtype Natural  is Integer range 0 .. Integer'Last;

We'll discuss more attributes later in the course.  In summary, discrete type means any integer or enumeration type.  (The only integer type we've learned about so far is the standard Integer.)

  • 'Pos converts from a discrete type to an integer.
  • 'Val converts from an integer to a discrete type.
  • 'Image converts from a scalar type to String.
  • 'Value converts from String to a scalar type.
  • 'First and 'Last take no parameter and give a scalar type.
  • 'Succ and 'Pred take a scalar type and give the same scalar type.

Also remember that a type name followed by ' denotes an attribute:

    Put(Character'Val(7)); -- beep

A type name followed by ( ) denotes conversion:

    G := F + Float(I);

And a type name followed by '( ) denotes qualification:



  1. Rainbow_Color'First
  2. Rainbow_Color'Image
  3. Rainbow_Color'Last
  4. Rainbow_Color'Pos
  5. Rainbow_Color'Pred
  6. Rainbow_Color'Succ
  7. Rainbow_Color'Val
  8. Rainbow_Color'Value
Which one of the above attributes would you use to convert from the String "Blue" to the Rainbow_Color Blue?

< prev   next >

No comments: