Recall from Chapter 9 that it is possible to construct sets from literals:
charSet1 := CharSet {'A' .. 'Z'}; digits := DigitType {0 .. 2};
and that
The Modula-2 notation Type {list} is said to be a constructor.
It is also possible to construct specific arrays and records from literals in a similar fashion, as shown in the following two sections.
Suppose a program has the declarations:
TYPE Vector = ARRAY [1..10] OF CARDINAL; VAR v1 : Vector;
In order to initialize such an array so that its components were, say, the values 1 through 10, one can write a loop, as shown earlier in the text:
FOR count := 1 TO 10 DO v1 [count] := count END;
However the same goal may be achieved using the constructor notation as follows.
v1 := Vector {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
If some of the components are to have the same value, repetition is shown using BY. That is:
v1 := Vector {1, 1, 1, 4, 4, 6, 7, 7, 7, 7};
is equivalent to:
v1 := Vector {1 BY 3, 4 BY 2, 6, 7 BY 4};
and all the components could be initialized to, say, zero using simply:
v1 := Vector {0 BY 10};
Note that all components must receive a value, so that
v1 := Vector {0 BY 9};
will cause the compiler to report an error. In addition, the values employed may be expressions, including the returns from functions, this if one had:
PROCEDURE NextCount () : CARDINAL; BEGIN INC (count); RETURN count; END NextCount;
then the constants 1 through 10 could be entered in the vector by
count := 0; v1 := Vector {NextCount() BY 10};
although this use of side-effects on a global variable is not generally a good idea.
Similar considerations apply to other arrays, so that:
TYPE String80 = ARRAY [0..79] OF CHAR; RealVector = ARRAY [1..10] OF REAL; VAR str : String80; rvect : RealVector;
could be initialized by such calls as:
chr := "o"; str := String80{"H", "e", "l" BY 2, chr, 0C BY 75}; rvect := RealVector {0.0 BY 10};
It is also quite proper to construct a constant of one of these types:
CONST NullStr = NameString {0C BY 80};
If the array is two dimensional, it is treated as an ARRAY OF ARRAY and the constructor notation is appropriately nested. The following declarations and statements are legal:
TYPE RowType = ARRAY [1..4] OF INTEGER; Array34 = ARRAY [1..3], [1..4] OF INTEGER; Array3R = ARRAY [1..3] OF RowType; (* last two have same structure *) VAR row : RowType; array1 : Array34; array2 : Array3R; array1 := Array34 { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; array1 := Array34 { {1, 2, 3, 4} BY 3 }; (* three rows the same *) array2 := Array3R {RowType{1, 2, 3, 4} BY 3}; (* same result *) array2 := Array3R { {0 BY 4} BY 3 }; (* all 12 entries are 0 *) row := RowType {1 BY 4}; array2 := Array3R { row BY 3 }; (* all 12 entries are 1 *)
Similar considerations apply if three or more dimensions are employed.
Records may also have constructed values, and this is particularly useful for initialization purposes. As arrays may have arrays as their components, so also records may have nested records, and therefore nested constructors. Given the declarations:
TYPE NameString = ARRAY [0..50] OF CHAR; Month = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec); Date = RECORD year: CARDINAL; month : Month; day : CARDINAL; END; Person = RECORD name : NameString; birthdate : Date; END; Group = ARRAY [1..100] OF Person; CONST startDate = Date {1, Jan, 1}; VAR moniker : NameString; da : Date; nullPerson, Joe : Person; class : Group;
the following are legal:
moniker := "Josephine"; da := Date {1901, Jan, 4}; nullPerson := Person {"", {1994, Oct, 15} }; Joe := Person {moniker, da }; Joe := Person {"Joe", da }; class := Group {nullPerson BY 100};
Observe that since the assignments to items of type Person are not assigning via constructors to the arrays, ordinary literal strings will do. This is certainly easier than:
Joe := Person { {"J","o","e", 0C BY 48 }, da };
where each character must be accounted for individually.