5.3 Indexed Data Types--Arrays

Several examples and problems thus far have involved the use of two or more related identifiers. These can be distinguished by including a numeric character in the identifier. One can employ x1, x2, y1, y2, or num1, num2, num3, or item1 and item2 for example.

Some of the mathematical ideas that were being modeled also had numbered data items, even though it was possible to avoid a requirement for using this idea in programs. For instance, the terms of a sequence are numbered a1, a2, a3, ... aj. This numbering scheme did not find its way into any of the examples because the abstractions of interest--the nth term and nth partial sum--could be computed by a closed form (formula) without needing to keep all the terms as program data items. There are times when this might be a good idea, especially if the data were to be stored in a file for later reference. For example, in the module SimplePay in section 5.2.1, it might have been a good idea to have variables with which to refer to the hours worked, one day at a time. The desired Modula-2 entities would model payMonday, payTuesday, payWednesday, and so on.

Data that is referred to with a single name along with an ordinal subscript is said to be indexed.

Most computing notations, including Modula-2, have a means to group together and refer to such collections of related items under a single identifier in this manner. The data structure employed has an inherent order imposed by the indices, and the latter are enclosed in brackets, because programming notations are not designed to recognize and attach meaning to subscripts, nor can they be typed on many machines. One writes, say, item [1], item[2], item [3], ... item [n] to refer to the specific objects in such a data structure.

A Modula-2 array is a collection of objects of the same underlying data type that is indexed by an ordinal type.

One declares an array in a TYPE statement by giving the range of indices that are permitted for the items, and by stating the underlying type of the entities that can be entered into the array. A single array may be declared in a VAR statement, but this is not recommended. Here are some examples to demonstrate the correct syntax:

  TYPE
    SmallArray = ARRAY [0 .. 10] OF INTEGER;
    Data  = ARRAY ['A' .. 'D'] OF REAL;
    Range = [1 .. 31];
    MonthPayGrid = ARRAY Range OF REAL;

  VAR
    smallArray1, smallArray2 : SmallArray;
    realArray : ARRAY ['A' .. 'D'] OF REAL; (* not a good idea *)
    dataArray1, dataArray2 : Data;
    payDays : MonthPayGrid;
    

and here are some assignments and expressions:

Correct:

  smallArray1 [1] := 0;
  payDays [12] := 12.34;
  dataArray1 ['A'] := 4.5;
  smallArray1 := smallArray2;	(* whole arrays can be assigned *)

Incorrect:

  smallArray1 [11] := 5;	(* out of range, no item #11 allowed*)
  dataArray1 ['A'] := realArray;	(* Error--Not of same type.*)
  dataArray1 := realArray;	(* Error--Not of same type.*)
  payDays [12] := 100;	(* wrong data type entered into array *)
  IF smallArray1 = smallArray2 	(* cannot be tested for equality. *)

NOTES: 1. The range of an array, which is enclosed in brackets in the declaration, gives the minimum and maximum index numbers or selectors that are allowed for the array type. A reference outside this range in the program generates an error.

2. All elements in an array must be of the same base type, but this can be any built-in or previously defined type, including another array. The complexity of a data type is up to the programmer trying to model a real-life situation.

3. When a variable is declared as an ARRAY without using a previously defined type name, its type is said to be anonymous. As the third example above shows, such a variable is not compatible with a named type, even if they are of the same underlying structure. This at least, is the Modula-2 rule; other notations have different rules.

4. The identifier of a range type can be used wherever a range is required. Such a range identifier is not enclosed in brackets, because it already has them.

Notice from the definition that any ordinal type, including an enumerated type, can be used as an array range. Thus,

  TYPE
    HoursArray = ARRAY [Mon .. Sat] OF REAL;
  VAR
    hours : HoursArray;

  BEGIN
    hours [Mon] := 4.51;

is a legal definition and use, provided that Mon .. Sat is a correct subrange of some previously defined enumerated type.

It is also worth noting that a function procedure cannot return an anonymous ARRAY type (one with no defined type). That is, one cannot write:

PROCEDURE ProcName(a : CARDINAL) : ARRAY [1 .. 5] OF CARDINAL; (* illegal *)

To achieve the desired result, it is necessary to write:

TYPE
  CardArray = ARRAY [1 .. 5] OF CARDINAL;

PROCEDURE ProcName (a : CARDINAL) : CardArray;

5.3.1 A First Look at String Variables

Some computing notations have a built-in type for string variables. Modula-2 lacks this type (which is why string variables have not previously been used in this text). However, Modula-2 does allow the user to declare an ARRAY [0 .. n] OF CHAR, to assign string literals to such arrays, and to use such arrays with input/output procedures such as ReadString and WriteString. The minimum selector in the range should be zero, and the maximum selector is then one less than the maximum number of characters in the string. Given the declarations

  TYPE
    String  = ARRAY [0 .. 80] OF CHAR;
    Range = [1 .. 10];
    SmallString = ARRAY [0 .. 5] OF CHAR;
    Paragraph = ARRAY Range OF String;

  VAR
    string1, string2 : String;
    sString : SmallString;
    ch : CHAR;
    para : Paragraph;

here are some assignments and expressions:

Correct:

  string1 := "Hello there";
  string2 := string1;
  sString := "Hello";
  sString := "H"; (* see warning below *)
  sString := ch;
  ReadString (string1);	(* allowed for any ARRAY range OF CHAR *)
  Write (string1 [2]);  (* individual array items are type CHAR *)
  WriteString (para [3]);
  para [1] := string1;

Incorrect:

  sString := "Hi there";	(* too long *)
  sString := string1; (* not the same type; one is too long for the other *)

In ISO Standard Modula-2 an ARRAY [0..0] OF CHAR (string of length one) is compatible with the type CHAR. The following is correct:

  TYPE
    string = ARRAY [0 .. 50] OF CHAR;

  VAR
    key : CHAR;
    str : string;

  BEGIN
    str := "Hi there fellow, how are you doing?";
    WriteString (str);
    WriteChar (str [1]);
    WriteString (str [1]);
    key := str [5];
    WriteChar (key);
    WriteString (key);

WARNING: Even though it is in ISO Modula-2, a single CHAR is not interpreted as a string of length one by some older or non-standard versions of Modula-2, and may not be assignable to a string variable (ARRAY range OF CHAR) in this way in such versions.

String literals and constants are said to be of the S-type, a supertype thought of as including all strings, of whatever length. Much more detail about strings will be provided in chapter 7. The student should consult that material before attempting to manipulate strings in any extensive way. If the only needs are for declaration, assignment of string literals, input and output, the information given in this section will suffice.


Contents