9.6 An Extended Example

Problem:

Write a program module that takes an input line of text and computes and prints the average number of consonants per syllable and the average number of syllables per word. Count every occurrence of one or more consecutive vowels as one syllable. (Note that this was posed as a problem in the exercise set of chapter 5, but could not be done there using sets.)

Problem suitability:

This can be viewed as an elaboration of the vowel counter problem in section 9.1. Provided that an appropriate rule for counting syllables is used, text of any size and from any source can be automatically examined.

Problem restatement:

Given
a line of text
To Compute
the number of consonants
the number of syllables
the number of words
Output Desired
the number of consonants per syllable
the number of syllables per word

Refinement:

Print an informative header.
Initialize counting variables to track consonants, syllables, and words.
Ask for a line of text to analyze.

Input the text line.
Examine each character:
  check for end of string
    if so, add one to word count and we're done
  check for blank
    if so, and previous one not a blank, add one to word count
  check for consonant
    if so, add one to consonant count
  check for vowel
    if so, and previous one not a vowel, add one to syllable count
    otherwise, go on to next character

Compute consonants per syllable and output it.
Compute syllables per word and output that, too.

Variables Needed:

To hold input characters - next, last : CHAR
As counters - cons, syll, word : CARDINAL
For output - consPerSyll, syllPerWord
Sets - Letters, Vowels, Consonants
Imports Needed: WriteString, WriteLn, SkipLine, WriteReal

NOTE: As in most extended examples by this point in the book, the user documentation has been omitted in order to save space. This cannot be done in student programs or in commercial ones.

Code:

MODULE TextLineStats;

(* Written by R.J. Sutcliffe *)
(* using ISO Modula-2  *)
(* last revision 1994 03 02 *)

FROM SRealIO IMPORT
  WriteReal;
FROM SWholeIO IMPORT
  WriteCard;
FROM STextIO IMPORT
  ReadString, WriteString, WriteLn, SkipLine;

TYPE
  CharSet = SET OF CHAR;
  String = ARRAY [0 .. 255] OF CHAR;
  
CONST
  blank = " ";
  
VAR
  next, last : CHAR;
  replyStr, theString : String;
  count, numChars, numOfCons, numOfSyll, numOfWords : CARDINAL;
  consPerSyll, syllPerWord : REAL;
  yesChars, Letters, Vowels, Consonants : CharSet;
  again : BOOLEAN;

BEGIN
 (* First print out header information. *)
  WriteString ("This program analyzes a line of text for");
  WriteString (" statistical information.");
  WriteLn;

  (* Next initialize the sets and other variables *)
  yesChars := CharSet {"Y", "y"};
  Letters := CharSet {'A' .. 'Z', 'a' .. 'z'};
  Vowels := CharSet
     {'A', 'E', 'I', 'O', 'U', 'Y', 'a', 'e', 'i', 'o', 'u', 'y'};
  Consonants := Letters - Vowels;

  REPEAT
    (* Now tell user to type the input. *)
    REPEAT
      WriteString ("Please provide line of text for analysis. ");
      WriteLn;
      ReadString (theString);
      SkipLine; (* gobble end of line state *)
      numChars := LENGTH (theString);
      IF numChars = 0
        THEN
          WriteString ("Type a proper string.  Try again.");
          WriteLn;
        END;
    UNTIL numChars > 0;

    (* initialize totals here, so can re-use *)
    numOfCons := 0;
    last := blank; (* ensure OK vowel count. *)
    numOfSyll := 0;
    numOfWords := 0;
    count := 0;
  
    REPEAT     (* loop back to here if not at end of text *)
      next := theString [count];
      IF (next = blank) AND (last # blank)
         (* check for word end *)
        THEN
          INC (numOfWords)
        ELSIF (next IN Consonants) THEN(* check for consonants *)
          INC (numOfCons)
        ELSIF (next IN Vowels) AND NOT (last IN Vowels) THEN
          (* check for vowel *)  (* no syll if 2 vowels *)
            INC (numOfSyll)
        END;     (* otherwise must be special char *)
  
      last := next;      (* reset last for next time around *)
      INC (count);
    UNTIL count > numChars;    (* actual check for end is here *)
  
           (* Now do processing for output *)
  
    INC (numOfWords); (* count last word; not followed by blank *)
    IF numOfSyll > 0
      THEN
        consPerSyll := FLOAT (numOfCons)/FLOAT (numOfSyll);
      END;
    syllPerWord := FLOAT (numOfSyll)/FLOAT (numOfWords);
    WriteLn;
    WriteString ("Number of consonants: ");
    WriteCard (numOfCons, 6);
    WriteLn;
    WriteString ("Number of syllables: ");
    WriteCard (numOfSyll, 6);
    WriteLn;
    WriteString ("Number of words: ");
    WriteCard (numOfWords, 6);
    WriteLn;
    WriteString ("Number of consonants per syllable: ");
    IF numOfSyll > 0
      THEN
        WriteReal (consPerSyll, 6);
      ELSE
        WriteString ("undefined.");
      END;
    WriteLn;
    WriteString ("Average number of syllables per word: ");
    WriteReal (syllPerWord, 6);
    WriteLn;
       (* get ready to loop around again or quit *)
    WriteString ("Do you wish to do another? (Y/N) ==> ");
    ReadString (replyStr);
    SkipLine;
    again := replyStr [0] IN yesChars; (* reply starts with 'y' *)
  UNTIL NOT again;

END TextLineStats.

Sample output:

This program analyzes a line of text for statistical information.
Please provide line of text for analysis. 
Now is the time for all good parties to come to the aid of women.

Number of consonants:     28
Number of syllables:     19
Number of words:     15
Number of consonants per syllable: 1.4737
Average number of syllables per word: 1.2667
Do you wish to do another? (Y/N) ==> n

Contents