Active Server Pages/Functions and Subroutines
In 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.
One 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?
A 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?
A 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.
Thus 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
By 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
The 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).
When 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 affect 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")
Just 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.
A 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 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.
Procedures 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.
- 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.
- 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.