## 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;

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
FROM SWholeIO IMPORT
WriteCard;
FROM SIOResult IMPORT

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;
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 *)
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 ==>");
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