Programming for Computing 07 - 08

Chapter: Writing Procedures (parameters, functions)

Module Admin   Contents, Index   Schedule  
1: Intro to VB    2: First Program and Projects    3: Variables, assignment, Strings    4: Type Conversion, InputBox, Constants    5:Built-in Functions    6: If - decisions    7: Loops - while, for    Example - find smallest    8: Scopes: local, global    9: Writing Procedures.Parameters, Functions   
10: Objects    11: Design    12: Testing    13: Graphics    14: Controls and events    15: Listboxes    16: Arrays    17: Files    18: The Command Line   

Appendices(links etc)   Additional Lectures    Tutorials (not in printed notes)     Selected solutions (not in printed notes)     Assignments    Additions and Errata   

new

The  schedule page has info on what we will do each week.

Why do we do this...

Programs nowadays can be millions of lines long. You will certainly meet ones with thousands of lines. How do humans understand such complex things? They split them into sections, and look at each section in turn. This is the role of procedures. Some programs we write in this intro unit might not warrant being split into procedures, but we do it anyway, just to get you used to the idea.

You may have heard of 'objects'. You will use them here, but not create them yourself.

Basically,an object his a set of related procedures which access the same item. For example, a VB button has a set of procedures, to alter its colour, size, text ... etc)

 


Section 9.0

Introduction

A procedure is a self-contained section of a larger program, i.e. a subtask. We may decide to decompose a large procedure into several smaller ones - for clarity, or to re-use one of the smaller procedures, or we may create a new class, which contains a set of related procedures.

A function is similar to a procedure, except it can return a result to the caller.

To understand procedures, you need to know about the scope of variables: local variables can be used only in the procedure they are declared in, but globals (declared in the ‘general’ section in VB can be used by any procedure.

The VB language has a large pre-written collection of procedures and functions - e.g. for strings, graphics,databases, web, speech recognition etc.

The stages we will go through here are:

  • procedures with no parameters (parameters are also known as arguments)
  • procedures with parameters passed by value
  • functions with parameters passed by value
 


Section 9.1

First Procedure Example

They really pay off in large programs but, to explain the concept, we have picked a small artificial example.

Problem: display "abc123abc456" in a series of message boxes, one character per box. This not a practical program - its purpose is to illustrate procedures.

Private Sub Button1_Click(etc...)
MessageBox.Show("a")
MessageBox.Show("b")
MessageBox.Show("c")
MessageBox.Show("1")
MessageBox.Show("2")
MessageBox.Show("3")
MessageBox.Show("a")
MessageBox.Show("b")
MessageBox.Show("c")
MessageBox.Show("4")
MessageBox.Show("5")
MessageBox.Show("6")
End Sub

Look at the code - you will see a repeated pattern:

MessageBox.Show("a")
MessageBox.Show("b")
MessageBox.Show("c")
we can identify these steps as a subtask of the problem. We will give these steps a name, and write them as a procedure. Here is how.

Just above the Private Sub Button1_Click line, we define the procedure:


'--------------------------------------
Private Sub letters()
    MessageBox.Show("a")      (vb auto-indents the middle.  I don't always do this!)
    MessageBox.Show("b")
    MessageBox.Show("c")
end sub
'---------------------------------------

  • Procedures begin with:
  • Private - to be used in your form only.
  • Sub - short for subroutine, or subtask
  • We have to think of a meaningful name for the task - we chose letters. You might have picked alphabet, firstThree etc Put ( ) after the name - 'empty brackets'
  • In VB, when you type: private sub something(), VB automatically gives you the matching end sub - so don't type it in.
  • To activate this subtask, we call it up, or 'invoke' it, as in:
    
    letters()
    
The complete code is now:
Public Class Form1 
    Private Sub letters()
        MessageBox.Show("a")
        MessageBox.Show("b")
        MessageBox.Show("c")
    End Sub
    '---------------------------------

    Private Sub Button1_Click()
    letters()
        MessageBox.Show("1")
        MessageBox.Show("2")
        MessageBox.Show("3")
        letters()
        MessageBox.Show("4")
        MessageBox.Show("5")
        MessageBox.Show("6")
    End Sub
End Class
  • Use F8 to step the code (position the form away from your VB window, before pressing the button. Note how the yellow execution point moves into the letters procedure, and back to the Button1Click procedure..
  • When you call a procedure, the program goes into it.
  • When its end sub is reached, it returns to where it left off, just below the call.
  • We talk about the 'flow of control' moving.
  • The example shows you how to define and call a procedure.
  • Note that we can call it as many times as we need. The call takes up one line, but the body of the called procedure can be long, and can include any type of instruction.
  • Note my '---------------------------- comment

    This makes it easier for humans to see the end of one procedure, and the start of another, when we print the code on paper.

 


Section 9.2

Here is an analogy of procedures:

Imagine an organisation. the manager, who will call up other workers (procedures) to perform subtasks. These workers in turn may call up lower-level workers to do tasks for them, etc. Each worker might need scraps of paper for rough work. Rather than requesting paper from the manager, we assume that they are capable of getting their own. This is the equivalent of local variables, which a procedure can use and create internally. Another approach might be for all the workers to share one large blackboard for rough work - the equivalent of global variables. Now consider one worker's task - perhaps it is to search a filing cabinet for a document. The search process (algorithm) will be the same, irrespective of the name of the document - we need to pass the name of the document to the worker. In VB we can pass arguments / parameters (data values) into a procedure for it to make use of.  


Section 9.3

Problem

1. Write a procedure called nameAndTown. It does 2 things: it displays your name in a message box, and then your town in a second message box. Call it three times from your click procedure. Note that even if we only call a procedure once, it might be still worth doing, as the program can be clearer for humans to read.  


Section 9.4

Using globals with procedures (poor, but we will work towards the ideal!)

Problem:
Write a program which inputs a valid number of minutes (must be 0 to 59) and a valid number of seconds (0 to 59). Any errors should cause a 'try again' message. Display the total number of seconds (e.g 2 mins 3 secs gives 123). This needs two loops, not nested.

We looked at a similar program in the Loops section. Here is the solution:

Private Sub Button1_Click()
Dim mins As Integer
Dim secs As Integer
Dim totalSeconds As Integer

mins = CInt(InputBox("type minutes"))
While (mins < 0) Or (mins > 59)
    mins = CInt(InputBox("0 to 59 only! - Try Again"))
End While

secs = CInt(InputBox("type seconds"))
While (secs < 0) Or (secs > 59)
    secs = CInt(InputBox("0 to 59 only! - Try Again"))
End While

totalSeconds = mins * 60 + secs
MessageBox.Show("Total seconds: " & totalSeconds)

End Sub
As a minor point, note that in the body of the loop, rather than put:

MessageBox.Show("Error!!")
mins = CInt(InputBox("Type minutes"))
we combined the error message and the prompt:

mins = CInt(InputBox("0 to 59 only! - Try Again"))
I guess you can spot the repeated pattern:
input...
while...
    input...
End While
We will parcel it up as a named procedure. The new bit is that the first time, it deals with minutes, the next time with seconds. We generalise the procedure so it uses a global variable, which we name as (e.g.) userReply
Dim userReply As Integer           'used for BOTH mins and secs


Private Sub readValue()
userReply = CInt(InputBox("type number"))
While (userReply < 0) Or (userReply > 59)
    userReply = CInt(InputBox("0 to 59 0nly! - Try Again"))
End While
End Sub
'------------------------------------------------

Private Sub Button1_Click()
Dim mins As Integer
Dim secs As Integer
Dim totalSeconds As Integer

readValue()
mins = userReply

readValue()
secs = userReply

totalSeconds = mins * 60 + secs
MessageBox.Show("Total seconds: " & totalSeconds)

End Sub
I hope you think that this is clearer. In fact, once you are sure that the readValue procedure works properly, you don't need to look at its code. All you need to put is:

readValue()
whatever... = userReply
 


Section 9.5

Problem - procedures (no parameters)

1. Extend the code to deal with hours - which must also be in range 0 to 59 - as well as mins and secs,

2. Using another global string variable (e.g. userPrompt), make the procedure display a specific message (e.g. "Enter Minutes", or "Enter seconds") instead of a more general prompt. You need to call the new procedure like this:


userPrompt = "Minutes"
readValue()
mins= userReply

3. Write a program which calculates the area of a shape (triangle or rectangle). The user enters the letter t or r, then the program asks for the needed dimensions. Use global variables to communicate between procedures.

The procedures should be:

  • getShape. Inputs the type of the shape into a global String variable named shapeType. It should reject incorrect input, and ask the user to re-enter.
  • areaTriangle. Asks user for dimensions(e.g. itsBase, itsheight - local) then displays result.
  • areaRectangle. Asks user for dimensions (e.g. itsHeight, itsWidth - local) and displays result.

4. Extend Q3 with a While, so that it keeps on asking the user for another shape, until told to stop. (Use an input box which inputs a String into reply, such as:


reply = inputBox("Want to calculate an area? (y/n" )
 


Section 9.6

Passing Parameters (or arguments) to procedures

Problem - extend the code to deal with hours - which must be in range 0 to 23 - as well as mins and secs.

The 23 is the problem! We cannot use our procedure as it uses 59! One fix is to use a global variable, as in:

dim limit as integer

Private sub readValue()
   ...
while  ...  (userReply > limit)
 
but the caller now has to put:
limit=23
readValue()
hours = userReply
this is messy! And did we not say to avoid globals?

Here is a solution with uses parameters (also known as arguments).

Instead of:

limit=23
readValue()
we allow the user to put:
readValue(23)
Here is how:

Dim userReply As Integer

Private Sub readValue(ByVal limit As Integer)
userReply = CInt(InputBox("type number"))
While (userReply < 0) Or (userReply > limit)
    userReply = CInt(InputBox("Too Big! - Try Again"))
End While
End Sub
'----------------------------------------------------------

Private Sub Button1_Click()
Dim mins As Integer
Dim secs As Integer
Dim hours As Integer
Dim totalSeconds As Integer

readValue(23)
hours = userReply

readValue(59)
mins = userReply

readValue(59)
secs = userReply

totalSeconds = hours * 3600 + mins * 60 + secs
MessageBox.Show("Total seconds: " & totalSeconds)

End Sub
Step F8 the code. When a call happens, look at the value of limit in the line:
Private Sub readValue(ByVal limit As Integer)

Note that it changes: 23, 59, 59 in each call.

Locate the 2 lines:

Private Sub readValue(ByVal limit As Integer)        'definition
and
readValue            (        23            )         'call
When the call happens, the 23 is assigned to limit behind the scenes. limit is a parameter, and we are 'passing a parameter' to the procedure.

Note that parameters are often variables, as in:

dim x as integer
x=200
readValue(x)     ' limit of 200
'or even
readValue(2 * x)    'limit of 400
In all programming languages, there is some intricacy with parameters. VB is in the middle as regards this.

We will pass every parameter in by value (not by reference - which VB can also do).

The byval parameters are INPUTS to the procedure. If we wish to return a value from the procedure, we will code it as a function instead. (see later)

When we define a procedure, as in:

private sub fred(byVal x as integer)
   etc
we have a 'formal parameter' list - here it just has one parameter - x.

The formal parameters are treated as local values within the procedure. The coder of the procedure has free choice of names for formal parameters.

When we invoke (call up) readValue, we supply an actual parameter, which might be an integer, or an integer variable, or an integer expression. The value of this is copied into x, and the procedure is executed. When it hits the end sub, it returns to where it left off.

If we have several parameters, the actual values are copied into the formal parameters in a left-to-right manner (actual 1 into formal 1, actual 2 into formal 2, etc.)

Note the use of 'values are copied'. This means that the original value is intact - the procedure CANNOT affect an input parameter if we use byVal.

The number of parameters supplied by the caller should match the number declared in the procedure.

Thus, readValue(23, 76) is wrong - 2 actual parameters, whereas the definition said one.

When we create arguments (or parameters) we use:

 byVal someName as someType, byval someName2 as someType, etc
VB will check for type mismatches. If we passed characters instead of an integer, as in:
	readValue("hello")
an error would result. readValue has one integer parameter.


(Advanced note: Analogy - you are working in a group, and you pass your essay part to a colleague for checking. Pass-by-value means that you xerox it, and give them a copy - the original is safe. Pass-by-reference implies that you give them the original, and if they lose it... well, tough!

Re- arrays. Arrays (later) are large structures, and they are passed by reference always. To pursue the analogy, your 200 page project is too time-consuming to copy.

end of note).


 


Section 9.7

Example - a procedure with two parameters

Our procedure works, but the prompts and error messages are non-specific (e.g. "Type number". It would be nice to have "Type Minutes", or "Type hours". We can do this with a second parameter - a string. Our code is:

Dim userReply As Integer
'-------------------------------------------------------------
Private Sub readValue(ByVal limit As Integer,ByVal prompt As String)
userReply = CInt(InputBox("Type " & prompt))
While (userReply < 0) Or (userReply > limit)
    userReply = CInt(InputBox(prompt & "Too Big! - Try Again"))
End While
End Sub
'------------------------------------------------------------
Private Sub Button1_Click()
Dim mins As Integer
Dim secs As Integer
Dim hours As Integer
Dim totalSeconds As Integer

readValue(23, "Hours")
hours = userReply

readValue(59, "Minutes")
mins = userReply

readValue(59, "Seconds")
secs = userReply

totalSeconds = hours * 3600 + mins * 60 + secs
MessageBox.Show("Total seconds: " & totalSeconds)

End Sub
For the first call of the procedure: 23 goes into limit

"Hours" goes into prompt.

The procedure is set up to expect an integer and a string, in that order.  


Section 9.8

Problems - procedure parameters

1. Modify readValue so it has a third parameter - the lower limit of the number. test it as in:
readValue(23, "Hour", 0)
readValue(65, "Age", 16)

2. Write a procedure named showName, which shows your name in a message box. Amend it so that it uses a loop to display your name a given number of times, e.g.

showName(4)
Each name should be displayed in a fresh message box.

Amend it so that it works with any name, e.g.

showName("Mike",4)
showName("joan", 2)

3. Write a procedure named showBig which has 2 integer parameters. It should display the bigger value in a message box. Use an IF inside the procedure.  


Section 9.9

Functions - Returning Values

We will now look at functions - which have input parameters, and return a result to the calling code.

We will make readValue even more convenient to use. Recall the built-in functions like sqrt:

n=Math.sqrt(25)
We will allow:
hours= readValue(23, "Hours")
instead of the use of a global, as we did here:
readValue(23, "Hours")
hours = userReply
We put:
Private Function readValue(ByVal limit As Integer, ByVal prompt As String) As Integer
Dim userReply As Integer
userReply = CInt(InputBox("Type " & prompt))
While (userReply < 0) Or (userReply > limit)
    userReply = CInt(InputBox(prompt & "Too Big! - Try Again"))
End While
Return userReply
End Function
'--------------------------------------------------------------------

Private Sub Button1_Click()
Dim mins As Integer
Dim secs As Integer
Dim hours As Integer
Dim totalSeconds As Integer

hours = readValue(23, "Hours")
mins = readValue(59, "Minutes")
secs = readValue(59, "Seconds")

totalSeconds = hours * 3600 + mins * 60 + secs
MessageBox.Show("Total seconds: " & totalSeconds)

End Sub
Note that there are no globals.

We call the function by e.g:

hours = readValue(23, "Hours")
Here are some incorrect calls:
readValue(23, "Hours")
readValue(23, "Hours")
- wrong because we are not doing anything with the result passed back from the function.

You can even use the result immediately, without storing it. The click procedure can be written as:

Private Sub Button1_Click()
Dim totalSeconds As Integer
totalSeconds = readValue(23, "Hours") * 3600 _
               + readValue(59, "Minutes") * 60 + readValue(59, "Seconds")
MessageBox.Show("Total seconds: " & totalSeconds)

End Sub
Such a solution is short, but the lack of variables can make it hard to read.  


Section 9.10

Splitting Long Lines

Note that we have a long line, so we have split it at a convenient point. Do this by picking the point, then pressing space, then underscore _ then enter.

 


Section 9.11

Details of functions

we use 'function' instead of sub.

VB creates an 'end function' line for us.

We decide what kind of thing the function will pass back to us - its output. Here we have an integer. This is specified by an AS after the last ')' of the parameters:

Private Function readValue(ByVal limit As Integer, ByVal prompt As String) As Integer
We have used a local variable userReply to hold the result. Just before the 'end function' we use the Return statement to exit from the method, taking with it the result.
Return userReply
This returns to where the function was called from, bringing back the value (the result of the function) so it can be used.  


Section 9.12

Function example - bigger

Let us write a function called bigger, which takes in two Doubles and returns the larger value.
' returns bigger of 2 Double parameters
private function bigger(byVal a as Double, _
                        byVal b as Double) as Double
dim answer as Double
if a>b then
    answer = a
else
    answer = b
end if
Return answer
end function
'----------------------------------------------------


private sub Button1_Click()
dim  x, y, z as Double
' get some values from user:
x=CDbl(inputBox("type a number"))
y=CDbl(inputBox("type a number"))
z=CDbl(inputBox("type a number"))

' a few example calls
z = bigger(x,y)
x = bigger(z-2 , 3*(x+y) )
MessageBox.Show(( CStr( bigger(z,10) )))
end sub
Note:
  • We specify the type of result that bigger returns, in the definition, after the (...).
  • we use a local variable to hold the answer, then pass the result back to the caller with an assignment to bigger.
  • Look at the second call of bigger:
    
      x = bigger( z-2 , 3*(x+y) )
    
    When the call takes place, the value of z-2 is copied into 'a', and the value of 3*(x+y) is copied into 'b'. The function then executes using the two copied values.
  • A function can have several inputs, and one output. As a picture:
    < src="pics/procbigger.jpg" alt="[pic of bigger]">
    Several inputs, one output

  • A brief 'comment has been placed above the function, giving an overview of what it does. This is sensible. We might also give info about the parameters, and author:
    'function bigger
    ' finds bigger of 2 Doubles.
    'Author: Mike Parr Aug 2010
    ' version 2
    
 


Section 9.13

Problems- function parameters

1. Write a function called readDoubleValue, which works in a similar way to readValue. Test it by:
dim myHeight as Double
myHeight = readDoubleValue(3.5, "Height")   ' 3.5 is max height allowed
MessageBox.Show( Cstr(myHeight))

2. A course has two assignments - the first one is worth 40%, the second is worth 60%. Write a function totalMark, which we can use like this:

' all variables integer
mark1 = readValue(100, "First Mark")
mark2 = readValue(100, "Second Mark")
total =  totalMark(mark1, mark2)    ' e.g totalMark(20, 60) gives 44    (8 + 36)

3. Write a function called 'smaller', which accepts two integer parameters, and which returns the value of the smaller one.

4. This Q is about building on existing code, rather than writing new functions from sceatch. Write a function called 'small3', which returns the smallest of 3 integer parameters. Try to make use of the existing 'smaller ' function from within small3. Here is how we wish to call small3:

x = small3(33, 67, 12)    ' returns  12

 


Section 9.14

Benefits of Functions and procedures

  • Programs with a lot of global variables are hard to understand or debug. Any variable could potentially be altered by any function. In the case of an error, it would be hard to find the incorrect function.
  • The use of procedures can avoid duplicated code. The program text is shorter - hence can be easier to read. The run-time size of the code is smaller as well, but memory size is not much of a problem nowadays.
  • Parameters/arguments are much clearer than the equivalent assignment statements. Additionally, recursion (not covered in this introductory text ) is much simpler if arguments are used.
  • design - later we will look at how procedures help in design.
Bearing this in mind, there are certain circumstances where a few global variables are useful - perhaps when a variable (or array) is used by most of the functions/procedures in a program. Removing it from the argument list of each function might result in a clearer program. Only experience will enable you to decide.

There is one guideline which covers most cases: when a variable is only used within one function/procedure, it should be declared locally.

 


Section 9.15

Advanced topic - Reference parameters

So far, we have seen that procedures and functions can have parameters ( arguments) - whose values are copied. In addition, functions can return a value can return a result. But what if a function needs to return several results ?

Here is an example: we will code a function which accepts two integer values, and returns their sum and difference.

We can't use a function, because it can only return ONE value - so we have to use 'pass by reference', in which we allow the function to modify parameters

We only need to pass the values of the two integers into the function; these values are not to be changed.

'  given a,b - calculates a+b , a-b
 private sub sumDiff(byVal a as integer, byVal b as integer, _
                     byRef sum as integer, byRef diff as integer)
    sum = a+b
    diff = a-b
End Sub
------------------------------------------

private sub cmdGo_click()
    dim c as integer, d as integer
    dim x as integer, y as integer
    dim add, subtract as integer

    c = 9			' assign demonstration values
    d = 12
    sumDiff(3,21,x,y)           'call 1
    sumDiff(c,d,add,subtract)   'call 2
end sub

Here is what takes place:

We pass the two input values by value - this is safer - we don't need to change these inputs.

The two outputs are passed by reference - behind the scenes, their addresses are passed - when a sub has the address of a variable, it can alter the value stored there.

Here is an incorrect call:

	sumDiff(3,21,x,6)
we are not passing a sensible address for the fourth parameter. The procedure needs a place to store the answers - a constant is not a suitable place!

Note the style of the call. We can't use sumDiff as part of an expression :

	n = sumDiff(3,21,x,y) +88  ‘  wrong- won’t compile
The way we have coded sumDiff determines that the call must make up a complete statement.

Java does not have passing by reference.  


Section 9.16

Reference or Value - choosing?

It is not possible to give exact rules - merely advice!
  • If your procedure/function returns nothing to the caller, then it should be a sub procedure, not a function. Any parameters should be passed byVal
  • If your procedure/function returns one simple value (an integer, a string... ,NOT an array) to the caller, it should be a function.
  • If your function returns more than one value to the caller, use byRef for the results.
  • When passing arrays, you have no choice - they are always passed byRef.
(end of advanced topic)

Key Points

  • Try to sub-divide programs into functions/procedures.
  • Ensure that the number, order, and type of parameters / arguments in the call match those in the definition .
  • Before writing a function, check the VB facilities to find out if it (or something similar) exists already.

Module Admin   Contents, Index