Using ordinals

When using ordinals, keep in mind these usage rules.

  • Ordinals are strongly-typed; that is, an ordinal can only be compared with or assigned to another ordinal of the same type. The ordinal must have been explicitly declared in a DECLARE statement.
  • The ordinal-type-name in a DEFINE ORDINAL statement cannot be used in comparisons or assignments.
  • Ordinals can be passed/received as arguments/parameters like any other data type.
  • Ordinals are invalid as arguments for all built-in functions requiring arguments with computational types. However, in support of ordinals, built-in functions have been defined and BINARYVALUE has been extended. These built-in functions are listed in Table 1. For descriptions of these functions, see Built-in functions, pseudovariables, and subroutines. Each of the built-in functions listed takes exactly one argument, which must be a reference having type ORDINAL.
Table 1. Ordinal-handling built-in functions
Function Description
BINARYVALUE Converts an ordinal to a binary value
ORDINALPRED Returns the next lower value for an ordinal
ORDINALSUCC Returns the next higher value for an ordinal
ORDINALNAME Returns a character string giving an ordinal’s name

Example 1: DO loops listing values from an ordinal definition

In the following sample code, the first DO loop lists, in ascending order, the members of the Color set; the second DO loop lists them in descending order. The example uses the ordinal definition from Example.

  dcl Next_color ordinal Color;

  do Next_color = first (:Color:)
                  repeat ordinalsucc( Next_color )
                  until (Next_color = last (:Color:));

    display( ordinalname( Next_color ) );
  end;

  do Next_color = last (:Color:)
                  repeat ordinalpred( Next_color)
                  until (Next_color = first(:Color:);

    display( ordinalname( Next_color));
  end;

The sample output for the first loop is as follows:

  RED
  ORANGE
  YELLOW
  GREEN
  BLUE
  INDIGO
  VIOLET

Example 2: Using ordinals with arrays

An ordinal cannot be used as an index into an array and cannot define an extent for a variable, including the lower or upper bound of an array. However, an ordinal can be converted to binary by the BINARYVALUE built-in function. The value that is returned by this function can then be used to index into an array or define an extent.

For example, the following package defines an array usage_count to hold the number of times each color is used, a procedure Record_usage to update this array, and a procedure Show_usage to display the values in this array.

  Usage: package exports(*);

    define ordinal Color ( Red,
                           Orange,
                           Yellow,
                           Green,
                           Blue,
                           Indigo,
                           Violet );

    dcl Usage_count(   binvalue( first(:Color:))
                     : binvalue( last(:Color:)) )
           static fixed bin(31) init( (*) 0 );
                /* first(:Color:)  = Red    */
                /* last(:Color:) = Violet */

  Record_usage: proc (Wall_color );
    dcl Wall_color type Color parm byvalue;

    Usage_count( binvalue(Wall_color) )
        = 1 + Usage_count( binvalue(Wall_color) );
  end Record_usage;

  Show_usage: proc;
    dcl Next_color type Color;

    do Next_color = Red upthru Violet;
      put skip list( ordinalname( Next_color) );
      put list( Usage_count( binvalue(Next_color) ));
    end;
  end Show_usage;

  end Usage;

Example 3: Using ordinals to create functions

Ordinals can be used to create functions that are easy to maintain and enhance and are as efficient as table look-ups.

In the following example, the function Is_mellow returns a bit indicating whether a color is or is not “mellow”. If more colors are defined, the “mellow” ones can be added to the list of colors in the select-group. In a select-group, unlike a hand-built table, the colors do not have to be in the same order as in the DEFINE statement, or in any particular order at all.

However, because all of the statements inside the select-group consist of RETURN statements that return constant values, the compiler will convert the entire select-group into a simple table look-up.

  Is_mellow: proc( Test_color ) returns( bit(1) aligned );

    dcl Test_color type Color parm byvalue;

    select (Test_color);
      when( Yellow, Indigo)
          return( '1'b );
      otherwise
          return( '0'b );
    end;

  end;

This feature can also be used to define your own version of the ORDINALNAME built-in function. Your own version can return the name you want to be displayed for each ordinal value. For example, the following function Color_name returns the color name associated with each name with the first letter capitalized:

  Color_name: proc( Test_color ) returns( char(8) varying );

    dcl Test_color type Color parm byvalue;

    select (Test_color);
      when ( Blue   ) return( 'Blue');
      when ( Green  ) return( 'Green');
      when ( Orange ) return( 'Orange');
      when ( Red    ) return( 'Red');
      when ( Yellow ) return( 'Yellow');
      otherwise return (");
    end;

  end;