To this point, little regard has been paid to the documentation of procedures. Comments have been included where appropriate, but nothing has been said about any special need to document the role and function of procedures. This needs attention, for procedures are not just portions of a program, or miniature programs on their own, but are pieces of re-usable code that may be called upon under a variety of circumstances. It is important therefore, that each procedure be clearly documented as to:

- its purpose
- any assumptions it makes about the data passed to it (
*preconditions*) - the nature of any data produced by it (
*postconditions*)

Such documentation will allow the programmer to avoid errors caused by using a poorly remembered piece of code for some purpose for which it was never intended; the proper use will be outlined in comments that remain with the code. Consider, for instance, a procedure designed to compute what percentage one number is of another:

PROCEDUREPercent (num, denom :REAL;VARresult :REAL);BEGINresult := 100.0 * num / denom;ENDPercent;

What will happen if this code is called with *denom* equalling zero? There are two ways to avoid the *divide-by-zero* run-time error lurking in indiscriminate use of this procedure. The first is to be up-front about the limitations of the procedure by documenting them:

PROCEDUREPercent (num, denom :REAL;VARresult :REAL); (* calculates what percentage num is of denom *) (* pre : denom # 0.0 post: result = 100.0 * num / denom *)BEGINresult := 100.0 * num / denom;ENDPercent;

If the procedure is written this way, it becomes the responsibility of the portion of code invoking it to check that the actual parameter passed to *Percent* is nonzero before going ahead with the procedure.

On the other hand, the responsibility for avoiding a run time error could be given to the procedure itself. If this is done, the main program must be informed when the result is not valid. The procedure could be written:

PROCEDUREPercent (num, denom :REAL;VARresult :REAL;VARok :BOOLEAN); (* calculates what percentage num is of denom *) (* pre : none post: if denom # 0.0 then result = 100.0 * num / denom and ok = true else result is undefined and ok is false *)BEGINIFdenom # 0.0THENresult := 100.0 * num / denom; ok :=TRUEELSEok :=FALSEENDENDPercent;

Preconditions and postconditions specified in the documentation of a procedure are calledpredicates.

Write a program module that can examine two lines, each determined from the coordinates of two points entered in from the keyboard, and determine whether the lines are parallel, perpendicular, or neither.

When two points are available in coordinate form P1(x1, y1) and P2(x2, y2), the slope of the line through the two points is given by

Two lines are parallel if they have the same slope, and they are perpendicular if their slopes are negative reciprocals. A horizontal line has zero slope, and the slope of a vertical line is undefined (it has no slope.) Thus, the failure of a procedure *Slope* to compute a result is not a bad thing; it just indicates that the line in question is vertical.

MODULESlopes; (* Written by R.J. Sutcliffe *) (* using P1MPWModula-2 for the Macintosh computer *) (* last revision 1991 02 26 *)FROMSTextIOIMPORTWriteString, WriteLn, ReadChar, SkipLine;FROMSRealIOIMPORTReadReal, WriteFixed;FROMSIOResultIMPORTReadResult, ReadResults;VARline1X1, line1Y1, line1X2, line1Y2, line2X1, line2Y1, line2X2, line2Y2, line1Slope, line2Slope:REAL; line1Ok, line2Ok :BOOLEAN; key :CHAR;PROCEDUREGetNum (VARtheNum :REAL); (* gets a number input from the keyboard Pre: none Post: if ok is true then return else ask for input to be re-entered *)VARok :BOOLEAN;BEGINREPEATWriteString ("Type the number here ==> "); ReadReal (theNum); ok := (ReadResult() = allRight); SkipLine;IFNOTok (* use saved value for testing *)THENWriteLn; WriteString ("error in input number; try again."); WriteLn;END;UNTILok;ENDGetNum;PROCEDURESlope (x1, y1, x2, y2:REAL;VARm:REAL;VARok:BOOLEAN); (* computes the slope of a line joining (x1, y1) and (x2, y2) Pre: none Post: if x1#x2 then m is the slope and ok is true else m is set to the maximum possible real; ok = false *)VARdeltaX, deltaY :REAL;BEGINdeltaX := x2 - x1; deltaY := y2 - y1;IFdeltaX # 0.0THENm := deltaY/deltaX; ok :=TRUE;ELSEm :=MAX(REAL); ok :=FALSE;END;ENDSlope;BEGIN(* main program *) WriteString ("This program computes the slopes of 2 lines"); WriteString (" from their coordinates."); WriteLn; WriteLn; (* obtain the coordinates *) WriteString ("First line:"); WriteLn; WriteString ("First point:"); WriteString ("x = "); GetNum (line1X1); WriteString ("y = "); GetNum (line1Y1); WriteString ("Second point:"); WriteString ("x = "); GetNum (line1X2); WriteString ("y = "); GetNum (line1Y2); WriteString ("Second line:"); WriteLn; WriteString ("First point:"); WriteString ("x = "); GetNum (line2X1); WriteString ("y = "); GetNum (line2Y1); WriteString ("Second point:"); WriteString ("x = "); GetNum (line2X2); WriteString ("y = "); GetNum (line2Y2); (* compute the slopes *) Slope (line1X1, line1Y1, line1X2, line1Y2, line1Slope, line1Ok); Slope (line2X1, line2Y1, line2X2, line2Y2, line2Slope, line2Ok); (* now output the data. *) WriteString ("The slope of the first line is ");IFline1OkTHENWriteFixed (line1Slope, 6, 0)ELSEWriteString ("undefined.")END; WriteLn; WriteString ("The slope of the second line is ");IFline2OkTHENWriteFixed (line2Slope, 6, 0)ELSEWriteString ("undefined.")END; WriteLn; WriteString ("The 2 lines are ");IFNOT(line1OkORline2Ok)OR(line1Slope = line2Slope)THENWriteString ("parallel.");ELSIF((line1Slope = 0.0)ANDNOTline2Ok)OR((line2Slope = 0.0)ANDNOTline1Ok)OR(ABS(line1Slope + (1.0 / line2Slope)) < 0.000001)THENWriteString ("perpendicular.");ELSEWriteString ("neither parallel nor perpendicular.")END; WriteLn; WriteString ("Press a key to continue ==>"); ReadChar (key);ENDSlopes.

**NOTES**: 1. MAX is a new standard identifier. It returns the maximum value of a type. This has not been done in this case to simulate a value of "infinity," but so that a line with no slope has some value attached to it for purposes of comparison.

2. Observe the comparison (ABS (line1Slope + (1.0 / line2Slope)) < 0.000001). It would be easier to write (line1Slope = - (1.0 / line2Slope)). However, this is unlikely to work in practice because of rounding off effects, so one must instead rely on the two values being close.

3. Observe also the order of checking for perpendicularity. If either of the first two conditions is true, the third will not be checked--a good thing if it could produce a divide-by-zero error.

Here is a single run from this module:

This program computes the slopes of 2 lines from their coordinates. First line: First point:x = please type here ===>1.0y = please type here ===>3.0Second point:x = please type here ===>3.0y = please type here ===>8.0Second line: First point:x = please type here ===>2.0y = please type here ===>4.0Second point:x = please type here ===>7.0y = please type here ===>2.0The slope of the first line is 2.500000 The slope of the second line is -0.4000000 The 2 lines are perpendicular. Press a key to continue ==>n