5.5 Manipulating Arrays

It is when it is used to manipulate arrays that the FOR loop comes into its own. Consider the oft-encountered problem of the initialization of variables. An array of numbers often must have all its elements set initially to a particular value (frequently zero). To illustrate, suppose a program had the declarations:

  CONST
    length = 50;
  TYPE
    MediumArray = ARRAY [1 .. length] OF INTEGER;

The desired procedure could be written:

  PROCEDURE Init (VAR theArray : MediumArray);
  VAR
    count : CARDINAL;

  BEGIN
    FOR count := 1 TO length
      DO
        theArray [count] := 0;
      END;
  END Init;

Likewise, if at some point the values in the array were all to be added, one could write:

PROCEDURE Add (VAR theArray : MediumArray) : INTEGER;
VAR
  count : CARDINAL;
  sum : INTEGER;
BEGIN
  sum := 0;
  FOR count := 1 TO length
    DO
      sum := sum + theArray [count];
    END;
  RETURN sum;
END Add;

NOTE: The details of using arrays as parameters are discussed in Section 5.6.

Problem:

Write a program module that counts the occurrences of the letters in text obtained from input from the keyboard. Have it also count the number of words and calculate an average to the nearest whole number. The program should print a chart showing the frequency of each printable character as well as the total number of characters, and the average number per word.

Discussion:

A complete refinement is not provided, but some of the recently introduced ideas are used. Notice that the occurrences of a letter are kept track of by incrementing the number in an array indexed by the character itself. Thus, the array element lets['e'] contains the number of times the letter e has been encountered. The solution makes use of four facts about the ISO/ASCII character sequence that may not be true of other character sets:

1. The sequence is numbered from 0 to 128.

2. Characters 0 to 31 and 127 are non-printing control characters.

3. Character number 32 is the space.

It also uses two values for ReadResultsendOfLine, and endOfInput the latter not previously employed

Algorithms:

The end of a word is handled as follows:

If one or more consecutive spaces or end of input then
  increment word counter

This is adequate, but notice that because punctuation marks are counted along with all the other printed characters, they are included in the average word length. The student is invited to improve on this program as an exercise.

MODULE LetterCounter;

(* Written by R.J. Sutcliffe *)
(* to illustrate the use of the for loop *)
(* using ISO Modula-2 *)
(* last revision 1991 02 27 *)

FROM STextIO IMPORT
  ReadString, ReadChar, WriteChar, WriteString, WriteLn, SkipLine;
FROM SWholeIO IMPORT
  WriteCard;
FROM SIOResult IMPORT
  ReadResults, ReadResult;

CONST
  space = CHR (32);
  cr = CHR (13);
  period = ".";
  min = 33; (* Set limits of printable ASCII characters *)
  max = 126;
  maxOnLine = 5;

TYPE
  LetArray = ARRAY CHAR OF CARDINAL;

VAR
  letterCount, wordCount, numOnLine, avPerWord : CARDINAL;
  lets : LetArray;    (* Examples:  lets ['A'], lets [","] *)
  ch, last : CHAR;
  lastResult : ReadResults;
  userDone : BOOLEAN;

BEGIN
  FOR ch := CHR (min) TO CHR (max) (* initialize totals to zero *)
    DO
      lets [ch] := 0;
    END;   (* for *)
  userDone := FALSE;

  WriteString ("Please type in text you want analyzed.");
  WriteString (" End with period at start of line.");
  WriteLn;

  wordCount := 0;
  letterCount := 0;
  last := space; (* now leading space won't be seen as words *)

  REPEAT   (* main loop to read text by characters *)
    ReadChar (ch);     (* reads whatever is next *)
    lastResult := ReadResult ();
    IF lastResult = endOfLine  (* translate end of line state *)
      THEN  (* into 'carriage return character read' *)
        ch := cr;
        SkipLine;
      END;
    IF (lastResult = allRight) AND (ch > space) AND (ch # period)
        (* no control characters counted *)
      THEN
        INC (lets [ch]);
        INC (letterCount);
      ELSIF ( (ch = space) AND (last # space) ) OR (ch = cr) AND (last # cr) THEN
                        (* two consec. is just one word *)
        INC (wordCount);
      ELSIF (ch = period) AND (last = cr) THEN
        userDone := TRUE;
        SkipLine;
      END;
    last := ch; (* reset last for next time *)
  UNTIL (lastResult = endOfInput) OR userDone;

(* now tell user the results, several to a line *)
  numOnLine := 0;
  WriteLn;
  FOR ch := CHR (min) TO CHR (max)
    DO
      WriteChar (ch);     (* put out character *)
      WriteCard (lets [ch], 5);    (* # of times it was there *)
      WriteString ("     "); (* leave some space; make columns *)
      INC (numOnLine );
      IF numOnLine MOD maxOnLine = 0
        THEN
          WriteLn;
        END;
    END;    (* for *)
  WriteLn;
  WriteLn;
  WriteString ("# of words = ");
  WriteCard (wordCount, 0);
  WriteLn;
  WriteString ("# of letters = ");
  WriteCard (letterCount, 0);
  WriteLn;
  IF wordCount # 0
    THEN
      avPerWord := TRUNC ((FLOAT (letterCount) / FLOAT (wordCount)) + 0.5);
      WriteString ("# of letters/word (nearest whole number) = ");
      WriteCard (avPerWord, 0);
      WriteLn;
    END;

  WriteString ("Press a key to end ==>");
  ReadChar (ch);
END LetterCounter.

When this program was run, the text file given it to analyze was its own source code. (This was done by using a macro program to type out the contents of the file when the prompt appeared asking for input.) Here are the results it produced:

!    0     "   18     #    8     $    0     %    0
&   0     '    5     (   62     )   62     *   42
+    1     ,   17     -    1     .    0     /    2
0   11     1    4     2    5     3    4     4    0
5    3     6    1     7    1     8    0     9    2
:   19     ;   63     <    0     =   33     >    2
?    0     @    0     A   23     B    2     C   40
D   22     E   25     F   15     G    1     H   14
I   24     J    1     K    0     L   33     M    9
N   29     O   34     P   10     Q    0     R   46
S   23     T   20     U    4     V    1     W   29
X    0     Y    2     Z    0     [    5     \    0
]    5     ^    0     _    0     `    0     a   90
b    5     c   45     d   42     e  174     f   16
g   13     h   35     i   81     j    1     k    5
l   65     m   21     n   94     o   76     p   21
q    0     r  125     s   79     t  156     u   47
v    7     w   19     x   11     y    8     z    3
{    0     |    0     }    0     ~    0

# of words =  515
# of letters =  2022
# of letters/word (nearest whole number) =  4

Contents