CHAPTER 11

Questions

1. Backtracking is the recursive use of trial-and-error steps through some kind of pattern, retracing the logical path back to the last success and then following a different branch.

2. The IF and CASE statements are the Modula-2 selection constructs.

3. One should opt for the CASE statement when: (i) the decision involves only the value of a single variable, (ii) there are several (but not very many) adjacent alternative values, (iii) the majority of the alternatives do NOT fall into the ELSE category.

4. CARDINAL has too many values, unless there is an optimizing compiler to reduce the size of the code.

5. The range in one case of a CASE statement cannot be interlaced with ranges in other cases.

6. Range checking can be turned off when error trapping code has been thoroughly tested.

7. Answers may vary.

8. In Modula-2, a pragma is a directive to the compiler that is included in the source file it is compiling. It gives the programmer a chance to give directions to the compiler while the code is being compiled. It is written in pragma brackets: "<*" and "*>"

9. Answers may vary.

10. Some strategies for reducing running time include: (i) Control Run-Time Checking (ii) Compile programs for specified environments (iii) Fine-tune loops (iv) link code segments or (v) use an optimized linker (the latter two are system dependent).

11. Answers may vary.

12. A program library is a collection of the code of one or more modules into a single file.

13. A structure constructor has the form typeName{values}. It is able to construct specific sets, arrays, and records using literals.

14.

	TYPE
	  realArray = ARRAY [1..10] OF REAL;
	VAR
	  reels : realArray;
	BEGIN
	  reels := realArray{0.0 BY 10};

15. The case separator is denoted by the "|" (vertical bar) character and is used to separate cases in a CASE statement. The statement separator is the ";" character and is used to denote the end of a statement in a block of code. Example for case and statement separators:
	CASE num OF
	  1:
	    <statement1>;  (* notice the semicolon used to separate statements *)
	    <statement2>;
	    <statement3>;  |  (* notice the vertical bar used to denote the end of a case *)
	  2:
	    <statement1>;
	END;

16. If none of cases match the value of case variable then an error will be generated at execution time. To avoid this problem, an ELSE clause can be attached to the CASE statement to handle any other values that are not handled by a case.

17. A variant record field is a record field that gives the option to select different fields from a CASE clause embedded in the record depending on the value of a tag variable. One would declare a variant field by using a case statement in a record with the expression as a record field and the cases as an optional record fields.

18. TYPE

	  Classification  = (single, married, divorce);
	  Date =
	    RECORD
	      year, month, day : CARDINAL;
	    END;

	  Employee =
	  RECORD
	    lastname : nameString;
	    firstname : nameString;
	    sex : SexType;  (* enumerated data type of M or F *)
	    dob : Date;
	    CASE marital : Classification OF
	      single : 
	        nextofKin : nameString; |
	        married :
	        marriageDate : Date;
	        spouse : nameString;
	        kids : CARDINAL; |
	      divorce:
	        divorceDate : Date;
	        nextofKin : nameString;
	      END; (* end CASE *)
	    CASE executive : BOOLEAN OF
	      TRUE:
	        salary : REAL;
	        car : REAL;
	        rank : RankString;  |
	      FALSE:
	        hourly : REAL;
	        rank : RankString;
	      END;  (* end CASE *)
	  END (* end RECORD *)

19. CONST

defSingle = Employee{"", BY 2, M, Date{0 BY 3}, single, "", TRUE, 0 BY 2, ""};

and there are 5 more to make a complete set.

20. Using a variant record can improve the simplify the structure of the RECORD. However, it can also make many nested CASEes that could make the programmer confused when debugging. If most of the fields in the records are identical, a variant form may be best.

21. Unrolling a loop is writing two or more repetitions of the steps in each iteration of the loop. This is done because some control structures are more efficient than repetition.

22. More efficient programs run faster, saving time and money. However, most efficiency improvements can be made with minimal changes to code, and determining the most efficient code technique may not be practical in any reasonable amount of time.

Problems

Note: Not all problems are shown. Most problems are left up to students as labs.

(*  Created
    June.22.1999
    Chapter 11, Question#26

    No error checking has been used for this program...be careful when inputting data!! *)

MODULE DataBase;

FROM STextIO IMPORT
  WriteString, ReadString, WriteLn, ReadChar, SkipLine;
FROM SWholeIO IMPORT
  ReadCard, WriteCard;
FROM SRealIO IMPORT
  ReadReal, WriteFixed;
FROM SeqFile IMPORT  (* use regular text so we can see what the file contains *)
  OpenWrite, OpenRead, ChanId, Close, OpenResults, read, write, old;
FROM Strings IMPORT
  Compare, CompareResults, Delete;
IMPORT TextIO;
IMPORT RealIO;
IMPORT WholeIO;

CONST
  min = 1;
  max = 10;
TYPE
  Name = ARRAY [0 .. 20] OF CHAR;
  MonthType = ARRAY [0..9] OF CHAR;
  Classification = (student, faculty, staff);
  Year = (freshman, sophomore, junior, senior);
  Rank = (instructor, assistant, associate, professor);
  Job = (secretary, maintenance, janitor);
  Date =
    RECORD
      year : CARDINAL;
      month : MonthType;
      day : [1 .. 31];
    END;   (* of the record Date *)
  Person =
    RECORD
      lastname, firstname : Name;
      birthdate : Date;
      (* use previous declarations for these. *)
      male : BOOLEAN;
      CASE status : Classification OF  (* variant part here *)
        student:
          idnumber : CARDINAL;
          year : Year |
        faculty:
          position : Rank;
          pay : REAL |
        staff:
          occupation : Job;
      END;  (* case *)
      married : BOOLEAN;
    END;  (* of the record Person *)
  RecType = ARRAY [min..max] OF Person;

VAR
  RecArray : RecType;
  option, current : CARDINAL;

PROCEDURE Menu (VAR option : CARDINAL);
(*   pre: none
    post: changes the value of option *)

BEGIN
  WriteString ("********************************");
  WriteLn;
  WriteString ("1.  Insert Record ");
  WriteLn;
  WriteString ("2.  Edit Record ");
  WriteLn;
  WriteString ("3.  Display Info ");
  WriteLn;
  WriteString ("4.  Save Record");
  WriteLn;
  WriteString ("5.  Get Record File ");
  WriteLn;
  WriteString ("6.  Exit ");
  WriteLn;
  WriteString ("********************************");
  WriteLn; WriteLn;
  WriteString ("Enter an option: ");
  ReadCard (option);
  SkipLine;
  WriteLn; WriteLn; WriteLn;
END Menu;

PROCEDURE Insert (VAR RecArray : RecType; currentRecNum : CARDINAL);
(*   pre: none
    post: Adds a record into the array *)

VAR
  tempcard : CARDINAL;
  tempreal : REAL;
  tempchar : CHAR;
  str : Name;
  str1 : MonthType;

BEGIN
  WITH RecArray[currentRecNum]
    DO
      WriteString ("Name:");
      WriteLn; WriteLn;
      WriteString ("Last Name: ");
      ReadString (lastname);
      SkipLine;
      WriteLn;
      WriteString ("First Name : ");
      ReadString (str);
      SkipLine;
      firstname := str;
      WriteLn;WriteLn;

      WITH birthdate DO
        WriteString ("Date of Birth: ");
        WriteLn; WriteLn;
        WriteString ("Month: ");
        ReadString (month);
        SkipLine;
        WriteLn;
        WriteString ("Day: ");
        ReadCard (tempcard);
        SkipLine;
        day := tempcard;
        WriteLn;
        WriteString ("Year: ");
        ReadCard (year);
        SkipLine;
      END;
      WriteLn; WriteLn;

      WriteString ("Male or Female (m/f): ");
      ReadChar (tempchar);
      SkipLine;

      IF CAP(tempchar) = 'M' THEN
        male := TRUE;
      ELSE
        male := FALSE;
      END; (* IF *)

      WriteLn; WriteLn;

      WriteString ("Classifcation: ");
      WriteLn;
      WriteString ("1. student");
      WriteLn;
      WriteString ("2. faculty");
      WriteLn;
      WriteString ("3. staff");
      WriteLn;
      WriteString ("Choice: ");
      ReadCard (tempcard);
      SkipLine;
      status := VAL (Classification, tempcard - 1);

      CASE status OF
        student:
          WriteString ("Give i.d. number, please. ");
          ReadCard (idnumber);
          SkipLine;
          WriteLn;
          WriteString ("and enter the year 1 .. 4 ");
          WriteString ("of studies. ");
          ReadCard (tempcard);
          SkipLine;
          year := VAL(Year, tempcard - 1) |
        faculty:
          WriteString ("Enter the rank of the faculty member ");
          WriteLn;
          WriteString ("by number.  A '1' for instructor, ");
          WriteLn;
          WriteString ("a '2' for assistant, a '3' for associate, ");
          WriteLn;
          WriteString ("or a '4' for a full professor. ");
          ReadCard (tempcard);
          SkipLine;
          position := VAL (Rank, tempcard - 1);
          WriteLn;
          WriteString ("How much is this faculty member paid? ");
          WriteLn;
          WriteString ("Answer using decimal point, please. ");
          ReadReal (pay);
          SkipLine; |
        staff:
          WriteString ("Please enter a '1' for a secretary, ");
          WriteLn;
          WriteString ("a '2' for a maintenance employee, ");
          WriteLn;
          WriteString ("or a '3' for a janitor. ");
          ReadCard (tempcard);
           SkipLine;
          WriteLn;
          occupation := VAL (Job, tempcard - 1);  (* no bar here *)
      END;  (* CASE *)

      WriteLn; WriteLn;

      WriteString ("Married (y/n)? ");
      ReadChar (tempchar);
      SkipLine;

      IF CAP(tempchar) = 'Y' THEN
        married := TRUE;
      ELSE
        married := FALSE;
      END;  (* IF *)

    END;  (* WITH *)

END Insert;

PROCEDURE Display (record : RecType; currentRecNum : CARDINAL);
(*   pre: none
    post: changes a displayed record *)

VAR
  count : CARDINAL;

BEGIN
  IF currentRecNum = 0 THEN
    WriteString ("No records available");
    WriteLn; WriteLn;
  ELSE

    FOR count := 1 TO currentRecNum DO
      WriteCard (count, 1);
      WriteString (".  ");

      WITH record[count] DO
        WriteString (lastname);
        WriteString (", ");
        WriteString (firstname);
      END;

      WriteLn;
    END; (* end FOR *)

  END; (* end IF *)
  WriteLn; WriteLn;
END Display;

PROCEDURE DisplayFull (RecArray : RecType; recnum : CARDINAL);
(*   pre: none
    post: displays full record information  *)

VAR
  class : Classification;

BEGIN
  WITH RecArray[recnum] DO
    WriteString ("Name: ");
    WriteString (firstname);
    WriteString (" ");
    WriteString (lastname);
    WriteLn;

    WITH birthdate DO
      WriteString ("Date of Birth:");
      WriteString ("  Month: ");
      WriteString (month);
      WriteString ("  Day:");
      WriteCard (ORD(day), 0);
      WriteString ("  Year:");
      WriteCard (year, 0);
      WriteLn;
    END;  (* end WITH *)

    WriteString ("Gender: ");

    IF male THEN
      WriteString ("Male");
    ELSE
      WriteString ("Female");
    END;

    WriteLn;
    WriteString ("Classification: ");

    CASE status OF
      student:
        WriteString ("Student");
        WriteLn;
        WriteString ("Id Number: ");
        WriteCard (idnumber, 1);
        WriteLn;
        WriteString ("Year: ");

        IF year = freshman THEN
          WriteString ("Freshman");
        ELSIF year = sophomore THEN
          WriteString ("Sophomore");
        ELSIF year = junior THEN
          WriteString ("Junior");
        ELSE
          WriteString ("Senior");
        END; |

      faculty:
        WriteString ("Faculty");
        WriteLn;
        WriteString ("Rank: ");

        IF position = instructor THEN
          WriteString ("Instructor");
        ELSIF position = assistant THEN
          WriteString ("Assistant");
        ELSIF position = associate THEN
          WriteString ("Associate");
        ELSE
          WriteString ("Professor");
        END;

        WriteLn;
        WriteString ("Salary: $");
        WriteFixed (pay, 2, 1); |
      staff:
        WriteString ("Staff");
        WriteLn;
        WriteString ("Job: ");

        IF occupation = secretary THEN
          WriteString ("Secretary");
        ELSIF occupation = maintenance THEN
          WriteString ("Maintenance");
        ELSE
          WriteString ("Janitor");
        END;

    END; (* end CASE *)

    WriteLn;
    WriteString ("Married: ");

    IF married THEN
      WriteString ("Yes");
    ELSE
      WriteString ("No");
    END;

    WriteLn; WriteLn;
  END; (* end WITH *)

END DisplayFull;

PROCEDURE SaveRec (record : RecType; numtosave : CARDINAL) : BOOLEAN;
(*   pre: none
    post: saves the array of records to a file specified by user *)
VAR
  file : ChanId;
  filename : ARRAY [0..12] OF CHAR;
  res : OpenResults;
  count : CARDINAL;

BEGIN
  WriteString ("Enter filename: ");
  ReadString (filename);
  SkipLine;
  WriteLn;
  OpenWrite (file, filename, read+write+old, res);
  IF res = opened THEN
    FOR count := 1 TO numtosave DO
      WITH record[count] DO
        (* save name *)
        TextIO.WriteString (file, lastname);
        TextIO.WriteString (file, " ");
        TextIO.WriteString (file, firstname);
	TextIO.WriteLn (file);

        (* save birthdate *)
        WITH birthdate DO
          TextIO.WriteString (file, month);
          TextIO.WriteString (file, " ");
          WholeIO.WriteCard (file, ORD(day), 1);
          TextIO.WriteString (file, " ");
          WholeIO.WriteCard (file, year, 1);
          TextIO.WriteLn (file);
        END;  (* end WITH *)

        (* save gender *)
        WholeIO.WriteCard (file, ORD(male), 1);
        TextIO.WriteLn (file);

        (* save classification *)
        WholeIO.WriteCard (file, ORD(status), 1);
        TextIO.WriteString (file, " ");

        CASE status OF
          student:
          WholeIO.WriteCard (file, idnumber, 1);
          TextIO.WriteString (file, " ");
          WholeIO.WriteCard (file, ORD(year), 1); |
          faculty:
          WholeIO.WriteCard (file, ORD(position), 1);
          TextIO.WriteString (file, " ");
          RealIO.WriteFixed (file, pay, 2, 1); |
          staff:
          WholeIO.WriteCard (file, ORD(occupation), 1);
        END; (* end CASE *)

        TextIO.WriteLn (file);
        (* save marital status *)
        WholeIO.WriteCard (file, ORD(married), 1);
        TextIO.WriteLn (file);
        TextIO.WriteLn (file);
      END; (* end WITH *)
    END; (* end FOR *)
    TextIO.WriteString (file, "<>");
    Close (file);
    RETURN TRUE;
  ELSE
    Close (file);
    RETURN FALSE;
  END; (* end IF *)
END SaveRec;

PROCEDURE GetRec (VAR record : RecType; VAR numofRecs : CARDINAL) : BOOLEAN;
(*   pre: none
    post: retrieves a record from a file and puts it in an array *)
VAR
  done : BOOLEAN;
  count, tempcard : CARDINAL;
  file : ChanId;
  res : OpenResults;
  filename : ARRAY [0..13] OF CHAR;

BEGIN
  WriteString ("Enter name of record file to retrieve: ");
  ReadString (filename);
  SkipLine;

  (* initialize variables *)
  numofRecs := 0;
  done := FALSE;
  count := 1;

  (* open file *)
  OpenRead (file, filename, read+write+old, res);
  IF res = opened THEN
    WHILE NOT done DO
      WITH record[count] DO
        (* get name *)
        TextIO.ReadToken (file, lastname);
        (* check for end of file *)
        IF Compare (lastname, "<>") # equal THEN
          TextIO.ReadString (file, firstname);
          TextIO.SkipLine (file);
          Delete (firstname, 0, 1);   (* to counter the extra space effect *)

          (* get birthdate *)
          WITH birthdate DO
            TextIO.ReadToken (file, month);
            WholeIO.ReadCard (file, tempcard);
            day := tempcard;
            WholeIO.ReadCard (file, year);
            TextIO.SkipLine (file);
          END;  (* end WITH *)

          (* get gender *)
          WholeIO.ReadCard (file, tempcard);
          male := VAL(BOOLEAN, tempcard);
          TextIO.SkipLine (file);

          (* get classification *)
          WholeIO.ReadCard (file, tempcard);
          status := VAL(Classification, tempcard);

          CASE status OF
            student:
              WholeIO.ReadCard (file, idnumber);
              WholeIO.ReadCard (file, tempcard);
              year := VAL(Year, tempcard); |
            faculty:
              WholeIO.ReadCard (file, tempcard);
              position := VAL(Rank, tempcard);
              RealIO.ReadReal (file, pay); |
            staff:
              WholeIO.ReadCard (file, tempcard);
              occupation := VAL(Job, tempcard);
          END; (* end CASE *)

          TextIO.SkipLine (file);

          (* get marital status *)
          WholeIO.ReadCard (file, tempcard);
          married := VAL(BOOLEAN, tempcard);
          TextIO.SkipLine (file);
          TextIO.SkipLine (file);
          INC (count);
          INC (numofRecs);
        ELSE
          done := TRUE;
        END;

      END; (* end WITH *)
    END; (* end WHILE *)
    Close (file);
    RETURN TRUE;
  ELSE
    Close (file);
    RETURN FALSE;
  END; (* end IF *)
END GetRec;

(* begin main block *)
BEGIN
  current := 0;
  REPEAT
    Menu (option);

    IF option = 1 THEN
      INC (current);
      Insert(RecArray, current);
    ELSIF option = 2 THEN
      Display (RecArray, current);

      IF current # 0 THEN
        WriteString ("Choose a record to edit: ");
        ReadCard (option);
        SkipLine;
        Insert (RecArray, option);
        option := 2;
      END;

    ELSIF option = 3 THEN
      Display (RecArray, current);

      IF current # 0 THEN
        WriteString ("Choose a record to display full info: ");
        ReadCard (option);
        SkipLine;
        DisplayFull(RecArray, option);
        option := 3;
      END;

    ELSIF option = 4 THEN

      IF SaveRec(RecArray, current) THEN
        WriteString ("Save successful");
      ELSE
        WriteString ("An error has occured couldn't save");
      END;

    ELSIF option = 5 THEN

      IF GetRec(RecArray, current) THEN
        WriteCard (current, 1);
        WriteString ("record(s) retrieved");
      ELSE
        WriteString ("Error occured when retrieved");
      END;

    END (* End IF *);

    WriteLn;
  UNTIL option = 6;
END DataBase.
(*  Created
    June.21.1999
    Chapter 11 Question 27 *)

MODULE NumberCheck;

FROM STextIO IMPORT
  WriteString, WriteLn, ReadChar, SkipLine;
FROM SWholeIO IMPORT
  ReadInt, WriteInt;
FROM SIOResult IMPORT
  ReadResult, ReadResults;

VAR
  number, count, sum : INTEGER;
  exit : CHAR;

BEGIN
  REPEAT
  (* get input *)
  WriteString ("Enter a whole number to be evaluated: ");
  ReadInt (number);

  IF ReadResult () # allRight THEN
    WriteString ("Not the right a valid number");
  END;

  SkipLine;
  WriteLn;
  (* initialize *)
  count := 1;
  sum := 0;

  (* evaluate if it's a perfect, deficitent, or abundant *)
  WHILE count < number DO

    IF number MOD count = 0 THEN
      sum := sum + count;
    END;

    INC (count);
  END;

  (* print *)
  WriteString ("The number");
  WriteInt (number, 0);
  WriteString (" is ");
  count := sum - number;

  CASE count OF
    0:
      WriteString ("PERFECT");|
    1..MAX(INTEGER):
      WriteString ("ABUNDANT");
    ELSE
      WriteString ("DEFICIENT");
    END;

  (* redo *)
  WriteLn;
  WriteString ("Do you want to do another (y/n)? ");
  ReadChar (exit);
  SkipLine;
  UNTIL CAP(exit) = 'N';
END NumberCheck.