4.3 Value and Variable Parameters (Introduction)

There have been two kinds of parameters used thus far in the examples of procedures. One kind is used to input data into the procedure only. When the procedure is called, the value represented by the actual parameter is copied into the memory location set aside for the formal parameter. At this point, there are two copies of the information in memory. If any changes are made to the local copy owned by the procedure (with the formal parameter name), the actual object copied by the calling routine is unaffected. Figure 4.3 illustrates.

A value parameter is a formal parameter naming a memory location that is set aside while the procedure is active, and into which the contents of an actual parameter will be copied at the time the procedure is called. The two copies of the information are decoupled (independent of one another).

The Action of a Call Using a Value Parameter:

  PROCEDURE Write (ch: CHAR); (* actual declaration *)
. . .
  character := "A";
  Write (character); (*call to this in a program *)

In some situations, one prefers to get things (often numbers) back from procedures, rather than have the procedure just take an action. To do this, one employs the second kind, a formal parameter whose name is simply attached to the same memory location that is already being used by the actual parameter. Any changes made to the formal parameter within the procedure body are automatically reflected in the actual parameter, because the two are simply different names for the same memory location. They are "tightly coupled". This action is illustrated in figure 4.4.

A variable parameter is a formal parameter whose name is attached to the same memory location as that named by the actual parameter at the time the call is made. The two names are coupled. These are distinguished from value parameters in the formal parameter list of the procedure heading by preceding them with the reserved word VAR.

The Action of a Call Using a Variable Parameter:

  PROCEDURE Read (VAR ch: CHAR); 	 (* declaration *) 
. . .
  WriteString ("Do you want to do this again?");
  Read (answer); 	 (* call to Read in a program *)

Here are some more examples:

  PROCEDURE GetNextDay (VAR day : CARDINAL);
  PROCEDURE Discrim (a, b, c : REAL;
				VAR d : REAL; VAR ok : BOOLEAN);

The procedure GetNextDay can (and presumably does) directly modify whatever variable is aliased to day by assigning a new value to day. Since both the actual parameter and the formal parameter name the same memory location, both names get new values at once.

The procedure Discrim is capable of directly modifying whatever is assigned to d and ok, but it can only use whatever data is copied to a, b, and c when it is invoked. Any changes it might make to these inside the procedure are not reflected in changes to the original entities owned by the calling routine that were copied to them.

Assuming that numberOfDay is a CARDINAL, w, x, y, and z are REAL and done is of type BOOLEAN, here are some legitimate calls to these two procedures:

GetNextDay (numberOfDay);
Discrim (w, x, y, z, done);
Discrim (3.0, x, 2.5, z, done);

Here are some that are not correct, with the reasons:

GetNextDay (3)   (* 3  is not a variable *)
GetNextDay (x)   (* x is not a cardinal *)
Discrim (5, x, y, z, done);   (* 5 is not real *) 
Discrim (w, x, y, 3.9, done);  (* 3.9 is not a variable *)
Discrim (w, x, y, done, z); (* wrong order of parameters *)

As the first and fourth of these incorrect examples illustrate, one must assign a variable to a variable parameter. After all, one cannot expect that a literal value will be somehow changed. (Can a 3 become not a 3?)

It is now possible ( figure 4.5) to diagram the parameter list completely.

Here is something else that might seem to be permitted, but that is not. Suppose cardNum is a CARDINAL, and in fact cardNum happens to equal 5, and that one has:

  PROCEDURE Fetch (VAR intNum : INTEGER)

Then a call Fetch (cardNum) produces a compiler error, because the types of an actual and formal VAR parameter must be identical, not merely assignment compatible, as are CARDINAL and INTEGER. Value parameters on the other hand, need only be assignment compatible.

To further illustrate the use of variable parameters, here is a procedure that swaps the values of two variables that are passed to it.

  PROCEDURE Swap (VAR firstNum, secondNum : REAL);

  VAR
    temp : REAL;

  BEGIN
    temp := firstNum;
    firstNum := secondNum;
    secondNum := temp;
  END Swap;

Notice the use of the local variable temp to hold the value of firstNum while the value of secondNum is assigned the name firstNum. The procedure Swap would be called by naming, as parameters, the two real variables whose values one wanted interchanged. The following sequence in the main program:

  rVar1 := 2.5;
  rVar2 := 7.8; 
  Swap (rVar1, rVar2);
  WriteReal (rVar1, 0);

would output 7.8 as the value of rVar1.

Example:

Write a program module that will sort and print in ascending order three reals input from the keyboard.

Restatement:

Refinement:

Ask for a number
Read it and assign to first variable
Ask for a number
Read it and assign to second variable
Ask for a number
Read it and assign to third variable

Sort the numbers
  if first is greater than second then
    swap the two
  if second is greater than third then
    swap the two
  if first is now greater than second then
    swap the two

Print the numbers out in order

As one looks this refinement over, it is apparent that reading and assigning are done three times with three different numbers and that there are potentially three swaps. Both subtasks (reading/assigning, and swapping) should therefore be procedures, so one makes this:

Further Refinement:

procedure 1
  Read a number
  Assign it to a variable parameter
procedure 2
  Swap two variable parameters
Main Program
  Call procedure 1 for the three numbers in turn.
  continue as outlined in the first refinement, but the swap is a procedure invocation.

Data Table:

Variables Required:
  procedure 1: only the single VAR parameter, and a boolean for recycling.
  procedure 2: two VAR parameters, and a temporary real.
  Main Program: three reals, and a character variable for carriage returns.

Imports Needed:
  ReadReal, WriteFixed, Done
  WriteString, WriteLn, ReadChar, SkipLine
  ReadResult, ReadResults

Here is the code:

MODULE SortThree;

(* Written by R.J. Sutcliffe *)
(* using P1 MPW Modula-2 for the Macintosh computer *)
(* last revision 1993 02 26 *)

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

VAR
  num1, num2, num3 : REAL;
  key : CHAR;

PROCEDURE GetNum (VAR theNum : REAL);

VAR
  ok : BOOLEAN;

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

PROCEDURE Swap (VAR firstNum, secondNum : REAL);

VAR
  temp : REAL;

BEGIN
  temp := firstNum;
  firstNum := secondNum;
  secondNum := temp;
END Swap;

BEGIN    (* main program *)
  WriteString ("This program sorts three numbers ");
  WriteString ("into ascending order.");
  WriteLn;
  WriteLn;
  (* obtain the three numbers *)
  WriteString ("First number: ");
  GetNum (num1);
  WriteString ("Second number: ");
  GetNum (num2);
  WriteString ("Third number: ");
  GetNum (num3);

  (* now sort the numbers. *)
  IF num1 > num2    (* check first two *)
    THEN   
      Swap (num1, num2);   (* trade if necessary *)
    END;

  IF num2 > num3    (* if second, third out of order *)
    THEN
      Swap (num2, num3);   (* trade them *)

      IF num1 > num2   (* and re-check new #1 & 2 *)
        THEN
          Swap (num1, num2);   (* trading if necessary *)
        END;     (* inner if *)

    END;    (* outer if *)

  (* numbers are sorted, so do print out *)

  WriteString ("In ascending order, the numbers are: ");
  WriteLn;
  WriteFixed (num1, 5,0);
  WriteLn;
  WriteFixed (num2, 5,0);
  WriteLn;
  WriteFixed (num3, 5,0);
  WriteLn;
  WriteString ("Press a key to continue ==>");
  ReadChar (key);
  
END SortThree. 

A run follows:

This program sorts three numbers into ascending order.

First number: Type the number here ===> 13.9
Second number: Type the number here ===> 11.98
Third number: Type the number here ===> 16.756
In ascending order, the numbers are: 
11.98000
13.90000
16.75600
Press a key to continue ==> q

Contents