6.3 Value Parameters

Earlier in the chapter we made a procedure called drawtriangle. We said it had some weaknesses. It would always draw the triangle in the same place and in the same colour. By using value parameters we can make the procedure a lot more flexible. We can pass information to the procedure about where to draw the triangle and what colour to use. As a first example let's modify our original drawtriangle to allow us to specify the colour.

procedure drawtriangle (triangleColour : int)
   drawline(100, 100, 150, 150, triangleColour)
   drawline(150, 150, 100, 200, triangleColour)
   drawline(100, 200, 100, 100, triangleColour)
end drawtriangle 

% main program
var s : string
drawtriangle(blue)
put "press enter to change the triangle to green"
get s:*
drawtriangle(green) 

Notice how we call the procedure now. We use the name of the procedure followed by brackets with the colour inside. This argument gets copied to the parameter in the procedure (the variable called triangleColour). So the first time the procedure is called triangleColour is blue. The second time it has the value green.

Now let's modify the procedure again so we can specify the location of the bottom vertex of the triangle. In the original procedure that vertex was at coordinates (100, 100).

procedure drawtriangle (x : int, y : int, triangleColour : int)
   drawline(x, y, x + 50, y + 50, triangleColour)
   drawline(x + 50, y + 50, x, y + 100, triangleColour)
   drawline(x, y + 100, x, y, triangleColour)
end drawtriangle 

% main program
var s : string
drawtriangle(100, 100, blue)
put "press enter to change the triangle to green and a new location"
get s:*
drawtriangle(300, 200, green) 

Copy this program and try running it. Try modifying the colour and location in the procedure calls.

You have to be careful that the type of the arguments and the type of the parameters match. The following procedure calls are all incorrect:

drawtriangle(40, 300, "green") % third argument should be a colour, ie. an int
                               % not a string
drawtriangle(green, 40, 300)   % tries to draw a triangle with lower vertex of (2, 40)
                               % since green has a value of 2 and a colour of 300 which
                               % is not allowed (maxcolor is 256)
drawtriangle(40, 300)          % wrong number of arguments.  Need to specify the colour.
drawtriangle(40.5, 300, green) % first argument should be an int 

In the first line of a procedure definition we list the parameters. We can use the same shortcuts that we use when we declared variables. In the drawTriangle example all the parameters are of type int. So we could change the first line of the procedure to: drawTriangle (x, y, triangleColour: int).

Here is an example where the types of the parameters are different:

%****** formatReal **************************************************
% Purpose: allows you to format a real number by specifying the     *
%          number of decimal places you want and whether you        *
%          want to go to a new line after printing                  *
% Parameters: 1) number - the number to be formatted                *
%             2) decimalPlaces - how many decimal places required   *
%             3) newLine - false if you want to stay on the current *
%                       line                                        *
%********************************************************************
procedure formatReal (number : real, decimalPlaces : int, newLine : boolean)
   if newLine then
      put number:1:decimalPlaces
   else
      put number:1:decimalPlaces..
   end if
end formatReal

% main program
var i : int := 5
var x : real := 123.456
var b : boolean := true

formatReal(x, i, b)
formatReal(12.4, 3, false)
formatReal(i, 2, b)
formatReal(543.2109, 3, true) 

Here is the output from this program:

123.45600
12.4005.00
543.211

Note that the first argument can be an int or a real (since a real variable can hold either type). The second argument must be an int and the third argument must be a boolean. In the exercises there will be a question that asks you to determine if various procedure calls to formatReal would be valid. Also note the comments at the start of the procedure. It is a good idea to do this for all your procedures. By describing what the procedure does and what its parameters are it makes your procedure easier to use.

It is also possible to use arrays as parameters. Here is a procedure that will print the elements in an array that stores 50 elements. The procedure will print the numbers ten to a line:

procedure printArray(numbers : array 1 .. 50 of int)
   for i : 1 .. 50
      put numbers(i):5 ..
      if i mod 10 = 0 then
         put ""
      end if
   end for
end printArray  

It is possible to make this procedure more flexible. You don't have to specify how large the array will be. We can use the asterisk (*) as a wild card to represent any size. We can use the function upper to see how large the arrray actually is when we print the elements in the procedure.

Here is a sample program with a modified version of printArray that can handle any size:

procedure printArray(numbers : array 1 .. * of int)
   for i : 1 .. upper(numbers)
      put numbers(i):5 ..
      if i mod 10 = 0 then
         put ""
      end if
   end for
   put""
end printArray 

var small : array 1 .. 4 of int := init (10, 20, 30, 40)
var large : array 1 .. 20 of int

for i : 1 .. 20
   large(i) := i * 3
end for

put "Here is the small array:"
printArray(small)
put "--------------------------------------------------"
put "Here is the large array:"
printArray(large) 

Here's the output from this program:

Here is the small array:
   10   20   30   40
--------------------------------------------------
Here is the large array:
    3    6    9   12   15   18   21   24   27   30
   33   36   39   42   45   48   51   54   57   60

Exercise 6.3

  1. State three reasons for avoiding the use of global variables in procedure definitions.
  2. For the procedure formatReal described in the last example, state which of the following calls are invalid. Justify you answer in each case.
    1. formatReal(1.2, 3)
    2. formatReal(3.45, 2, true)
    3. formatReal(5, 1.2, false)
    4. formatReal(1.2, 3, "false")
    5. formatReal(true, 3.45, 2)
    6. formatReal(3, 2, true)
  3. The following program uses a mixture of local variables, global variables, and parameters. What is the output of the program?
    var a, b : int
    
    procedure fiddle(a : int)
       var c : int
    
       b := a + 1
       c := b + 2
       put a, " ", b, " ", c
    end fiddle
    
    a := 5
    b := 7
    put a, " ", b
    fiddle(a)
    put a, " ", b
    fiddle(b)
    put a, " ", b 
    1. Write a procedure putArea that has two real value paramters length and width. The procedure should find and print the area of the rectangle having the given length and width, with the answer rounded to the nearest integer.
    2. Test your procedure in a program that reads a sequence of pairs of real values representing the lengths and widths of various rectangles.
  4. Write a procedure putRow that will print a given character a specified number of times. The character to be printed and the number of times that it is to be printed should be parameters of the procedure.
  5. Use the procedure that you have written for Question 5 to write a procedure putTriangle that prints a triangle like the one shown, centred across the page.
                  *
                 ***
                *****
               .     .
              .       .
             *** ... ***
    The number of rows in the triangle and the symbol to be printed should be parameters of the procedure while the page width should be defined as a constant in the procedure.

    Test your procedures by using them in the following program:

    procedure putRow( ...
    % definition of putRow should be here
    
    procedure putTriangle( ...
    % definition of putTriangle should be here
    
    put "A small triangle of stars"
    putTriangle(3, '*')
    put ""
    put "A large triangle of plus signs"
    putTriangle(10, '+')
    put ""
    put "A very small triangle of dots"
    putTriangle(1, '.')
    put ""
    put "A triangle too small to be seen"
    putTriangle(0, 'A')
    put ""
    put "A challenge"
    putTriangle(-2, '#') 
  6. Given the procedure heading
    procedure range (list : array 1 .. * of int) 
    complete the definition so that range finds the least and greatest values in list and prints them.