5.8 Manipulating Multi-Dimensional Arrays

This is done in much the same way as single-dimensional arrays, using FOR loops. Here, one makes use of the fact that repetition structures such as FOR loops can be nested one inside the other in a Modula-2 program. Suppose one had the declaration:

TYPE
  Matrix = ARRAY [0 .. rowmax], [0 .. colmax] OF CARDINAL;
VAR
  A : Matrix;

and wished to initialize all the elements to some value, initNum. Two FOR loops, one nested inside the other are written in the following manner:

FOR rowCount := 0 TO rowmax
  DO
    FOR columnCount := 0 TO colmax
      DO
        A [rowCount, columnCount] := initNum;
      END;      (* for column *)
  END;    (* for row *)

The outer of the two loops (FOR rowCount) steps through the rows, one at a time. For each of the rows, the inner (FOR columnCount) loop steps through the columns one at a time. In this fashion, each element is initialized in a systematic manner. This routine can be made generic as a procedure by using a two-dimensional open array as follows:

  PROCEDURE InitMatrix (VAR theMatrix : ARRAY OF ARRAY OF CARDINAL; initNum : CARDINAL);

  BEGIN
    FOR rowCount := 0 TO HIGH (theMatrix)
      DO
        FOR columnCount := 0 TO HIGH (theMatrix [0])
          DO
            theMatrix [rowCount, columnCount] := initNum;
          END;      (* for column *)
      END;    (* for row *)
  END InitMatrix;

Multidimensional arrays, though not open ones, are used in the following

Problem:

Design a program that employs a data structure that will record the number of male and female freshmen, sophomores, juniors and seniors enrolled in each of the Sciences, Humanities, Social Sciences, Education, Physical Education, Business, and Aviation divisions at a typical small university. Write the declarations, the code to fill the data structure, and the code to print it out in tabular form. Print also the breakdown of the total number of students by sex, year, and division.

Restatement:

Given: data in the 56 distinct categories

Required to do: prepare summaries by major categories

Desired output: a complete listing of the data with summaries

Discussion of Data Structure:

At some stage during the refinement of the problem (close to the beginning), the programmer must make a decision on the kind of data structure to be used. Since this is a section on multi-dimensional arrays, it takes little imagination to arrive at a suitable way of doing it in this case.

Pseudocode:

Obtaining Data from user:
For each class
  for each division
    ask the user for number of men and women
    enter this into the data structure

Generating summaries:
For each class
  sum the men and women in all divisions
For each division
  sum the number of men and women in all classes
For each sex
  sum the number from all classes and divisions

Data output:
For each class
  print an informative heading
  for each division
    print an informative heading
    print the number of men
    print the number of women

Data Table:

Enumerated types:

Array Types:

Main variables:

Imports required:

From STextIO:

From SWholeIO:

From SIOResult:

Organization:

No attempt has been made to abstract separate tasks from the whole into procedures, as all the work is closely tied to a single array variable that holds all the program data.

Code:

MODULE EnrollData;

FROM STextIO IMPORT
  WriteString, WriteLn, ReadString;
FROM SWholeIO IMPORT
  ReadCard, WriteCard;
FROM SIOResult IMPORT
  ReadResults, ReadResult;

TYPE
  ClassName = (Freshman, Sophomore, Junior, Senior);
  DivName  = (Science, Humanities, SocialScience, Education, 
             PhysEd, Business, Aviation);
  SexName  = (male, female);
  ClassTot  = ARRAY ClassName OF CARDINAL;
  DivTot   = ARRAY DivName OF CARDINAL;
  SexTot   = ARRAY SexName OF CARDINAL;
  MainData = ARRAY ClassName, DivName, SexName OF CARDINAL;
  String   = ARRAY [0 .. 18] OF CHAR;

VAR
  (* arrays *)
  classTotals : ClassTot;
  divTotals : DivTot;
  sexTotals : SexTot;
  mainDataArray : MainData;
  (* counters *)
  classCount : ClassName;
  divCount : DivName;
  sexCount : SexName;
  (* strings for program use *)
  classNames : ARRAY ClassName OF String;
  divNames : ARRAY DivName OF String;
  sexNames : ARRAY SexName OF String;
  (* could have done types for last three too *)

BEGIN  (* main program *)
  (* First initialize all strings. *)
  classNames [Freshman] := "freshman  ";
  classNames [Sophomore] := "sophomore ";
  classNames [Junior] := "junior    ";
  classNames [Senior] := "senior    ";
  divNames [Science] := "Science    ";
  divNames [Humanities] := "Humanities ";
  divNames [SocialScience] := "Soc Science";
  divNames [Education] := "Education  ";
  divNames [PhysEd] := "PhysEd     ";
  divNames [Business] := "Business    ";
  divNames [Aviation] := "Aviation    ";
  sexNames [male] := "males ";
  sexNames [female] := "females ";

  (* Now initialize the main array *)

  FOR classCount := Freshman TO Senior
    DO
      FOR divCount := Science TO Aviation
        DO
          FOR sexCount := male TO female
            DO
              mainDataArray [classCount, divCount, sexCount] := 0;
            END;    (* for sexCount *)
        END;    (* for divCount *)
    END;    (* for classCount *)

  (* Now write the main loop to gather this data. *)

  FOR classCount := Freshman TO Senior
    DO
      FOR divCount := Science TO Aviation
        DO
          FOR sexCount := male TO female
            DO
              REPEAT
                WriteLn;
                WriteString ("Please give the number of ");
                WriteString (classNames [classCount]);
                WriteLn;
                WriteString (sexNames [sexCount]);
                WriteString (" in the ");
                WriteString (divNames [divCount]);
                WriteString (" division ==> ");
                ReadCard (mainDataArray [classCount, divCount, sexCount]);
              UNTIL ReadResult () = allRight;
               (* keep trying until correct entry *);
            WriteLn;
            END;    (* for sexCount *)
        END;    (* for divCount *)
    END;    (* for classCount *)

  (* data is all here now, so do summaries *)

  FOR classCount := Freshman TO Senior     (* by class *)
    DO
      classTotals [classCount] := 0;    (* initialize to 0 *)
      FOR divCount := Science TO Aviation
        DO
          classTotals [classCount] := classTotals [classCount] 
                   + mainDataArray [classCount, divCount, male] 
                   + mainDataArray [classCount, divCount, female];
        END;    (* for divCount *)
    END;    (* for classCount *)

(* include code here to total by division and by sex in the same way *)

  WriteString ("Summary  by class:");
  WriteLn;
  WriteString ("class      number");
  WriteLn;
  FOR classCount := Freshman TO Senior
    DO
      WriteString (classNames [classCount]);
           (* write class name headings *)
      WriteCard (classTotals [classCount], 10);
      WriteLn;
    END;    (* for classCount *)

  (* include code here to print the appropriate headings and final data by faculty and by sex in the same way *)

END EnrollData.

NOTES: 1. The completion of this example is left for the exercises at the end of this chapter, as sufficient detail has been presented for illustrative purposes. What is present here is free from syntax errors, but since it has not been completed and run, it is not guaranteed to be free of logical errors, and the logic of the ReadCard statement needs work. Why?

2. A Modula-2 implementation is likely to have a fixed maximum permissible degree of loop nesting. This affects any combination of FOR, WHILE, REPEAT and any other similar programming structures. This limitation is rarely a problem for the student, whatever the figure may be.

3. Notice the painstaking initialization of all those strings. For the purpose to which they are put here, there is no alternative to this, as one cannot use strings (or any but ordinal types) in an enumeration. However, there are other ways of initializing arrays (including strings) and these are covered in section 11.6.1


Contents