4.2 Writing and Calling Procedures

A procedure is a miniature of a program in that it may have its own constants, variables and procedures. In fact, almost anything that can be part of a program module except imports can also be included in a procedure. Here is the general form:

  PROCEDURE NameOfProcedure  ( <parameter list with types > );
  < local declarations including local procedures go here >
  BEGIN
    (* statement sequence goes here *);
  END NameOfprocedure;

This is also shown in figure 4.2. When the procedure is invoked, one writes:

  NameOfProcedure ( <parameter list> ).  (* no types *)

The full definition of the parameter list can be found in figure 4.5. Here are some partial declarations with only the procedure headings:

  PROCEDURE Power (base : REAL; exponent : CARDINAL; VAR ans : REAL);
  PROCEDURE Max (num1, num2, num3 : INTEGER; VAR result : INTEGER);
  PROCEDURE WriteReal (realNum : REAL; fieldLength : CARDINAL);

with some corresponding calls in a program:

  Power (theBase, theExp, theAns);
  Power (15.0, 6, theAns);
  Max (first, second, third, answer);
  Max (3, -2, 18, answer);
  WriteReal (2.75, 15);

Here is another complete, if somewhat short example. This procedure accepts for its parameters two numbers and calculates and prints the percentage that the first is of the second.

  PROCEDURE PrintPercent (firstNumber, secondNumber: REAL);
  BEGIN
    WriteReal (firstNumber, 10);
    WriteString (" is ");
    WriteReal (100.0 * firstNumber/secondNumber, 10);
    WriteString (" percent of ");
    WriteReal (secondNumber, 10);
    WriteLn;
  END PrintPercent;

Two calls to this procedure are:

  PrintPercent (mark, total);
  PrintPercent (15.0, 74.3);

As can be seen from these brief examples, when a procedure is declared, the names and the types of all the parameters employed by the procedure must be stated as part of that declaration. When the procedures are actually called, the parameters must be stated, but the types of those parameters are not given again. Here are two definitions:

The list of parameters provided in the declaration of a procedure is known as a formal parameter list. The list employed when the procedure is invoked is the actual parameter list.

WARNING: The actual parameters used when a procedure is called must match the formal parameters both in the order in which they are given and in their type.

For instance, given the declaration above, one could not use either of the following calls:

  Power (15, 6, theAns);  (* tries to pass cardinal to real *)
  Power (15.0, 6.2, theAns); (* tries to pass real to cardinal *)
  Power (15, theAns);  (* missing a parameter *)

It is also worth pointing out that:

  PROCEDURE Max (num1, num2, num3 : INTEGER; VAR result : INTEGER);

is equivalent to

  PROCEDURE Max (num1: INTEGER; num2: INTEGER; num3 : INTEGER; VAR result : INTEGER);

Here is an example of a module to calculate and print the area and perimeter of either a square or a circle given the side length or radius as the case may be. It is formulated with separate procedures for the two calculations, and for obtaining the data.

MODULE Areas;

(* Written by R.J. Sutcliffe *)
(* to illustrate procedures *)
(* using P1 Modula-2 for the Macintosh computer *)
(* last revision 1993 02 25 *)

FROM STextIO IMPORT
  WriteString, ReadChar, WriteLn, SkipLine;
FROM SRealIO IMPORT
  WriteReal, ReadReal;
FROM SIOResult IMPORT
  ReadResult, ReadResults;

VAR
  dimension, mArea, mPerim : REAL;
  which, ans : CHAR;
  again : BOOLEAN;

(*********************************************)
PROCEDURE GetNum (VAR theNum : REAL);
VAR
  readOK : BOOLEAN;

BEGIN
  REPEAT
    WriteString ("Type the number here ==> ");
    ReadReal (theNum);
    readOK := (ReadResult() = allRight);
    IF NOT readOK
      THEN
        WriteLn;
        WriteString ("error in input number; try again.");
        WriteLn;
      END;
    SkipLine;
  UNTIL readOK;
END GetNum;

PROCEDURE CalcSquare (side : REAL; VAR area, perim: REAL);

BEGIN
  area := side * side;
  perim := 4.0 * side;
END CalcSquare;

PROCEDURE CalcCircle (radius : REAL; VAR area, perim: REAL);

CONST
  pi = 3.141592653;
  twopi = 2.0 * pi;

BEGIN
  area :=pi * radius * radius;
  perim := twopi * radius;
END CalcCircle;
(*************************************************)

BEGIN    (* main program *)
  WriteString ("This program calculates areas and perimeters of");
  WriteLn;
  WriteString ("squares from a side length ");
  WriteString ("or circles from the radius.");
  REPEAT
    WriteLn;
    (* Present menu, give user the choice. *)
    WriteString ("Do you want to work with ");
    WriteString ("a circle or a square? ");
    WriteLn;
    WriteString ('Type a "C" or an "S" here ===> ');
    ReadChar (which);
    SkipLine;
    which := CAP ( which);
    WriteLn;

    (* now obtain a set of data *)
    WriteString ("What is the dimension of the figure ?");
    WriteLn;
    GetNum (dimension);
    
    IF which = 'C' 
      THEN    (* Now go do one. "S" is the default *)
        CalcCircle (dimension, mArea, mPerim);
      ELSE
        CalcSquare (dimension, mArea, mPerim);
      END;    (* if *)

    WriteString ("The area is ");
    WriteReal (mArea, 0);
    WriteString (" square units and ");
    WriteLn;
    WriteString ("the perimeter is ");
    WriteReal (mPerim, 10);
    WriteString (" units.");
    WriteLn;
    WriteLn;
    
    WriteString ("Do another (Y/N) ==>");
    ReadChar (ans);
    SkipLine;
    again := (CAP (ans) = "Y");
  UNTIL NOT again;

END Areas. 

Here is a run from this module:

This program calculates areas and perimeters of
squares from a side length or circles from the radius.
Do you want to work with a circle or a square? 
Type a "C" or an "S" here ===> s
What is the dimension of the figure ?
Type the number here ==> zz
error in input number; try again.
Type the number here ==> 3.4
The area is  1.1560000E+1 square units and 
the perimeter is 13.60000000 units.

Do another (Y/N) ==>y
Do you want to work with a circle or a square? 
Type a "C" or an "S" here ===> s
What is the dimension of the figure ?
Type the number here ==> 2.7
The area is  7.2900004 square units and 
the perimeter is 10.80000000 units.

Do another (Y/N) ==>y
Do you want to work with a circle or a square? 
Type a "C" or an "S" here ===> c
What is the dimension of the figure ?
Type the number here ==> 5.0
The area is  7.8539818E+1 square units and 
the perimeter is 31.41593000 units.

Do another (Y/N) ==>n

Note that the necessary imports WriteReal, WriteLn, WriteString are all done in the main program module. These entities, and any others defined in the main Module (but not ones hidden away inside another procedure) are all visible to and available for the use of any procedure in the program. Likewise, anything defined inside a procedure (including its own parameters) is visible and usable within the procedure itself, and within any procedures defined inside it.

A program module and a procedure both define a scope of visibility for the entities they define. Such entities are usable inside all other procedures defined within that scope, but not outside it.

That is, procedures exist in a kind of hierarchy of scopes. This text will not have much occasion to make use of this fact until later, and a detailed examination of scope, visibility, and their consequences will be undertaken in chapter ten.

Also note that, while in theory a procedure may have any number of parameters; in practice, it is wise to write procedures with, say, no more than four--or to use more procedures. Otherwise code becomes cumbersome, confusing and hard to maintain.


Contents