Active Server Pages/Functions and Subroutines
Objectives
editIn this chapter we will introduce the concepts of creating procedures. A procedure may be a function or subroutine definition. Once defined, a procedure can be called from anywhere in your code. After reading this chapter, you should understand how to write your own procedure and then call that from another point in your code.
Content
editOne of the most powerful features of the Active Server Pages is the ability to create powerful procedures that can extend the scripting language. If you are new to procedural programming, you can think of a procedure as a macro that performs a complex task. Once defined and included in your web page, you can reference the macro in any code block you define.
For example, if you created a function to display the current date and time you could call this function with something like:
<%= TodaysDate() %>
What is a Function?
editA function is a procedure that returns a value to the caller. By "caller", we mean the place in the ASP code where you invoke the procedure through a "function call". Functions are declared using the Function statement and terminated with an End Function statement.
' a totally useless function to get the current date and time
Function TodaysDate
TodaysDate = Now()
End Function
Above, we have an example of a function definition that will return the current date and time using the built-in function Now. By assigning a value to a variable which has the same name as the function (TodaysDate), we return a value to the calling process. There is no return statement like the PHP scripting language or in C programming.
This function can be declared anywhere in your web page and it will be made available for you to call from within anywhere in the page. As we will see later, you can even place commonly used procedures within a Server-Side Include page so that you don't have to write duplicate procedures on multiple pages from your web site.
This function is totally useless because it's easier to just call the built-in function Now.
It is important to note that a procedure declaration like this will not do anything meaningful until it is actually called. It simply declares a procedure which will do work when it is called.
What is a Subroutine?
editA subroutine is a procedure that does not return a value to the caller. Subroutines are declared using the Sub statement and terminated with an End Sub statement.
' a subroutine to output today's date
Sub ShowDate
Response.Write("Today's Date is: ")
Response.Write(Now())
End Sub
The subroutine call above will output text that looks like "Today's Date is: Jan, 23 2004 06:43:12AM" when it is called. The location in the web page where this is output depends on where the subroutine is called. You should call the subroutine at the exact point where you would like it to output the text.
Unlike a function declaration, you are not allowed to assign a return value to the procedure name (ShowDate). If you attempt to use a subroutine as a function, you will receive a runtime error. The same is true when you try to use a function as a subroutine.
Procedure Parameters
editThus far, we have only looked at procedures which have no parameters. A parameter is an ASP value which is passed to the procedure for the purpose of completing a task. An optional parameter list is declared along with the function or subroutine by appending a comma-separated list of parameter names and enclosing this within parentheses.
' a subroutine to say hello
Sub SayHello(sName)
Response.Write "Hello "
Response.Write sName
Response.Write ", Nice to Meet You!"
End Sub
Above we have a very simple subroutine which takes a single argument. This argument is the name of the person you want to say hello to. When you invoke this subroutine with a call such as:
<% SayHello("Bill Gates") %>
Your ASP page will output "Hello Bill Gates, Nice to Meet You!". In this case we pass a string literal as the argument to the subroutine. You may also pass an ASP variable or a complex expression.
' a subroutine to convert hours/minutes to seconds
Function HoursToSeconds(nHours, nMinutes)
HoursToSeconds = nHours * 3600 + nMinutes * 60
End Function
Finally, this example shows how multiple parameters are declared for a function. These two values are used to compute the number of seconds from the hours and minutes. You can see that multiple parameters are separated by a comma. If you want to place a long list of parameters on multiple lines, you will have to use the "line continuation" operator which is the underscore character (_).
Passing by Reference
editBy default, all procedure parameters are passed by reference. This means that when you use a variable as your procedure argument, any changes to the parameter within the procedure, will be reflected in the original argument. If you want to explicitly declare your parameter as "by reference", you may use the ByRef keyword:
' explicitly pass by reference
Sub HelloInformal(ByRef sFullName)
If InStr(1, sFullName, " ") > 0 Then
sFullName = Left(sFullName, InStr(1, sFullName, " ")-1)
End If
Response.Write "Hello "
Response.Write(sFullName)
End Sub
You need to be careful when writing and invoking procedures like these. This procedure will actually modify the calling argument by removing everything except the first word in the argument.
Passing by reference is the default calling method. So even if the ByRef keyword is not present, the parameter is defined as "by reference" by default. It is important to remember this so that you don't fall into the trap mentioned above.
Passing by Value
editThe other way to pass values into your procedure is "by value". This means that a copy of the value is taken when the procedure is called. The parameter holds a copy of the value so that any modifications to this parameter will not affect any arguments that were used by the calling process.
Parameters which should be "passed by value" should be declared using the ByVal prefix. By default, all parameters are passed "by reference". If you need to pass a value "by value", you need to prefix the parameter declaration with ByVal.
' explicitly pass by value
Sub HelloInformal(ByVal sFullName)
If InStr(1, sFullName, " ") > 0 Then
sFullName = Left(sFullName, InStr(1, sFullName, " ")-1)
End If
Response.Write "Hello "
Response.Write(sFullName)
End Sub
When invoking this subroutine call, you will be guaranteed that the original argument will not be modified. You won't have that same guarantee with the previous declaration which declares the parameter ByRef.
You should only use ByVal when it is absolutely necessary. It is more efficient to pass parameters "by reference" than it is to pass them "by value". In typical use, most parameters are used as "read only", so you will probably only use the ByVal keyword occasionally.
Of course, you can mix the ByRef and ByVal prefixes together on a list of multiple parameters. Each parameter in your list can have one prefix modifying it (or none at all).
Calling Conventions
editWhen invoking a subroutine or function, you need to be aware of the different calling conventions used for each. If you fail to follow these guidelines, you will receive a syntax error when loading the page in your browser.
Calling a function will always require you to enclose the arguments within parentheses. Unlike subroutines, a function can be used within expressions or as an argument to another function.
' call a function
nSeconds = ToSeconds(2, 35)
' use a function call within an expression
nSeconds = 3600 + ToSeconds(2, 35)
' use a function as an argument to another function
sTime = MyFormatTime(ToSeconds(2, 35))
' ignore the return value for a function call
ToSeconds(2, 35)
In the last example above, you will notice we call the function but ignore the return result. This is only useful when your function has some side effect on a global variable, or modifies a variable passed "by reference".
Calling subroutines, on the other hand, requires that you do not enclose the arguments within parentheses. The one exception to this rule is when you precede your function call with the Call keyword. The two different invocations are shown below
' call a subroutine (parentheses forbidden...)
SayHello "Bill Gates"
' parentheses allowed for subroutine call using "Call"
Call SayHello("Bill Gates")
Local Variables
editJust as you can declare variables using the Dim statement in your web page, you can also declare variables for use within your procedures. These are typically referred to as "local variables" because they are only available "locally" within the procedure declaration. The variables declared outside of the procedures can be thought of as "global variables".
You declare local variables within a procedure block just as you would a regular variable:
' example of local variables as loop iterator
Sub CountTo(nFinal)
Dim I
For I = 1 To nFinal
Response.Write "count = " & I & "<br>"
Next
End Sub
The scope of the variable I is strictly within the procedure declaration. You won't be able to reference I outside of the procedure. It is created when the function is called and disappears when the function finishes.
Active Server Pages allows you to reference global variables within your procedures. Although this is not recommended because it makes your procedures less portable, there are some instances where this proves useful.
Variable Hiding
editA variable may be declared "locally" and "globally" using the same name. This is perfectly legal in ASP. When you do this, the local variable will "hide" the global variable within the context of the procedure.
' I declared globally and locally
Dim I
I = 5
Response.Write "I = " & I & "<br>"
CountTo 10
Response.Write "I = " & I & "<br>"
Sub CountTo(nFinal)
Dim I
For I = 1 To nFinal
Response.Write "count = " & I & "<br>"
Next
End Sub
In the example above, we have the variable I declared globally (actually the scope is the life of the web page) and locally. When you run this sample code, you will notice that the global variable I remains unchanged after the call to CountTo. This is because the local variable is completely separate from the global. Changes to the local variable have no effect on global I.
Because you declared a local variable with the same name as a global one, you effectively hide the reference to the global variable. Within the procedure CountTo, there is no way to reference the global I. While other languages provide a mechanism to access such variables, ASP does not. One way to get around this is to either: change the name of your local variable or pass the global variable as a parameter (using a different name).
Recursion
editRecursion refers to the practice of having a function "call itself" in order to perform a repetitive task. Creating a recursive function is as simple as declaring a function that contains a call to itself.
' A simple bubble-sort routine
Sub BubbleSort(arrList, nSpan)
Dim I, vTemp
If nSpan = 0 Then nSpan = UBound(arrList)
For I = 0 To UBound(arrList) - nSpan
If arrList(I) > arrList(I + nSpan) Then
vTemp = arrList(I + nSpan)
arrList(I + nSpan) = arrList(I)
arrList(I) = vTemp
End If
Next
If nSpan > 1 Then BubbleSort arrList, nSpan-1
End Sub
As you can see on the last line of the subroutine, the bubble sort routine will call itself recursively as the span decreases by one each time. The function will stop calling itself once the span distance reaches one. The "depth" or "level of recursion" depends on the size of the array passed. If you have seven elements, the procedure will call itself 6 times.
Shown below is some ASP code which will test the bubble sort routine be creating an array of seven numbers and invoking the BubbleSort procedure:
Response.Write "<p>"
Dim arrTest, I
arrTest = Array(8753, 5234, 434, 5234, 34, 1, 65, 123)
For I = 0 To UBound(arrTest)
Response.Write "arr("&I&") = " & arrTest(I) & "<br>"
Next
BubbleSort arrTest, 0
Response.Write "</p>"
Response.Write "<p>"
For I = 0 To UBound(arrTest)
Response.Write "arr("&I&") = " & arrTest(I) & "<br>"
Next
Response.Write "</p>"
You should notice, that we first call the procedure with a span of zero. Code in the procedure will recognize this as a signal to initialize the span parameter with for sorting.
You should also be aware, that the bubble sort routine outlined above could easily be re-written to use no recursion at all. A good rule-of-thumb is: if the procedure is much easier to write and maintain using recursion then go ahead and do so.
Summary
editProcedures allow you to create reusable code "macros" that perform a specific task. Functions are procedures which return a value, whereas subroutines do not. Procedures may be declared anywhere within a page and referenced from anywhere on your page. Output from a subroutine or function will be placed at the location where the sent to the location where the procedure is invoked.
Each procedure declaration may have an optional list of parameters. Parameters may be declared as being passed "by reference" or "by value". The default parameter passing convention is "by reference". Use ByVal or ByRef to explicitly declare your procedure parameters.
Review Questions
edit- Procedures may be subroutines or functions.
- Only functions may return a value to the caller.
- Function declares a function, Sub declares a subroutine.
- Procedures may be placed anywhere on the page from which they are called.
- Commonly-used procedures may be placed in a Server-Side Include file.
- An optional parameter list may be declared after the procedure name.
- The parameter list must be enclosed in parentheses.
- Multiple parameters are separated by a comma.
- The default parameter passing convention is "by reference".
- ASP variables passed "by reference" to a procedure may be modified.
- The default parameter passing convention is "by reference".
- When calling functions, enclose the arguments in parentheses
- When calling subroutines, use no parentheses around the arguments
- Functions may be used in expressions and as arguments to functions
- Local variables may be declared inside of a procedure block.
- Local variables hide references to global (page-scoped) ones.
Exercises
edit- Write a function to compute the diameter of a circle (2 * pi * radius) with the single parameter being the radius.
- Write a function to compute the area of a rectangle (length * width) having two parameters.
- Write a subroutine to output a greeting such as: "Good Morning", "Good Afternoon" or "Good Evening" based on the time-of-day.
- Re-write the bubble-sort procedure as a non-recursive function.