Subroutines group lines of code into meaningful pieces of work. A function is a subroutine that returns a value. The use of subroutines and functions facilitates testing, code reuse, and readability. This in turn reduces errors.
The keyword Sub defines the beginning of a subroutine, and End Sub defines the end of a subroutine.
Sub FirstSub Print "Running FirstSub" End Sub
To use a subroutine, place the name of the subroutine that you want to call on a line. The name can optionally be preceded by the keyword Call.
Sub Main Call FirstSub ' Call Sub FirstSub FirstSub ' Call Sub FirstSub again End Sub
Subroutine and function names must be unique in a module. They are subject to the same naming conventions as variables , including the use of spaces in their names .
Sub One [name with space] End Sub Sub [name with space] Print "I am here" End Sub
Compatibility | Visual Basic allows a subroutine to be preceded by optional keywords such as Public or Private. In OOo Basic all subroutines and functions are public. Currently, OOo Basic does not allow you to explicitly specify the scope of a routine as Public or Private, but this is scheduled to be supported in OOo 2.0. |
The keyword Function is used to declare a function which, like a variable, can define the type it returns. If the type is not declared, the return type defaults to Variant. You can assign the return value at any point and as many times as you want before the function ends. The last value assigned is returned.
Sub test Print "The function returns " & TestFunc End Sub Function TestFunc As String TestFunc = "hello" End Function
A variable that is passed to a routine is called an argument. Arguments must be declared. The same rules for declaring variable types apply to declaring argument types.
A routine name can optionally be followed by parentheses, both when it is defined and when it is called. A routine that accepts arguments can optionally enclose the argument list in parentheses. The argument list follows the routine name on the same line. Blank space is allowed between the name of the routine and the argument list. See Listing 25 .
Sub ExampleParamTestl() Call ParamTestl(2, "Two") Call ParamTestl 1, "One" End Sub Sub ParamTestl(i As Integer, s$) Print "Integer = " & i & " String = " & s$ End Sub
By default, arguments are passed by reference rather than by value. In other words, when the called subroutine modifies an argument, the caller sees the change. You can override this behavior by using the ByVal keyword. This causes a copy of the argument (rather than a reference to the argument) to be sent (see Listing 26 and Figure 6 ).
Sub ExampleParamValAndRef() Dim i1%, i2% i1 = 1 : i2 = 1 ParamValAndRef(i1, i2) MsgBox "Argument passed by reference was 1 and is now " & il & CHR$(10) &_ "Argument passed by value was 1 and is still " & i2 & CHR$(10) End Sub Sub ParamValAndRef(iRef%, ByVal iVal) iRef = iRef + 1 ' This will affect the caller iVal = iVal - 1 ' This will not affect the caller End Sub
Warning | Constants passed as arguments by reference cause unexpected behavior if their value is modified in the called routine. The value may arbitrarily change back. For example, I had a subroutine that was supposed to decrement an Integer argument in a loop until it was zero; the argument never became zero. |
Compatibility | Visual Basic supports the optional keyword ByRef. This keyword is not supported by OOo Basic. Because passing by reference is the default behavior, the ByRef keyword was considered redundant. |
You can declare arguments as optional by preceding them with the keyword Optional. All of the arguments following an optional argument must also be optional. Use the IsMissing function to determine if an optional argument is missing. See Listing 27 .
REM Make test calls with optional arguments. REM Calls with Integer and Variant arguments should yield the same result. REM Unfortunately, they do not. Sub ExampleArgOptional() Dim s$ s = "Variant Arguments () => " & TestOpt() & CHR$(10) &_ "Integer Arguments () => " & TestOptI() & CHR$(10) &_ "---------------------------------------------" & CHR$(10) &_ "Variant Arguments () => " & TestOpt() & CHR$(10) &_ "Integer Arguments () => " & TestOptI() & CHR$(10) &_ "---------------------------------------------" & CHR$(10) &_ "Variant Arguments (1) => " & TestOpt(1) & CHR$(10) &_ "Integer Arguments (1) => " & TestOptI(1) & CHR$(10) &_ "---------------------------------------------" & CHR$(10) &_ "Variant Arguments (,2) => " & TestOpt(,2) & CHR$(10) &_ "Integer Arguments (,2) => " & TestOptI(,2) & CHR$(10) &_ "---------------------------------------------" & CHR$(10) &_ "Variant Arguments (1,2) => " & TestOpt(1,2) & CHR$(10) &_ "Integer Arguments (1,2) => " & TestOptI(1,2) & CHR$(10) &_ "---------------------------------------------" & CHR$(10) &_ "Variant Arguments (1,,3) => " & TestOpt(13) & CHR$(10) &_ "Integer Arguments (1,,3) => " & TestOptI(13) & CHR$(10) MsgBox s, 0, "Optional Arguments of Type Variant or Integer" End Sub REM Return a string that contains each argument. If the argument REM is missing, then an M is used in its place. Function TestOpt(Optional vl, Optional v2, Optional v3) As String TestOpt = "" & IIF(IsMissing(vl), "M", Str(vl)) &_ IIF(IsMissing(v2), "M", Str(v2)) &_ IIF(IsMissing(v3), "M", Str(v3)) End Function REM Return a string that contains each argument. If the argument REM is missing, then an M is used in its place. Function TestOptI(Optional il%, Optional i2%, Optional i3%) As String TestOptI = "" & IIF(IsMissing(il), "M", Str(il)) &_ IIF(IsMissing(i2), "M", Str(i2)) &_ IIF(IsMissing(i3), "M", Str(i3)) End Function
Compatibility | Although Visual Basic .NET no longer supports the IsMissing function, it provides another mechanism for specifying default values to be automatically assigned to missing parameters. |
You can omit any optional arguments. Listing 27 demonstrates two functions that accept optional arguments. The functions are the same except for the argument types. Each function returns a string containing the argument values concatenated together. Missing arguments are represented by the letter "M" in the string. Although the return values from TestOpt and TestOpt1 should be the same for the same argument lists, they are not (see Figure 7 ). This is a bug.
Bug | The IsMissing function returns incorrect results for variables that are not of type Variant when the missing argument is followed by a comma. |
Compatibility | VBA supports default values. OOo Basic does not, but is scheduled to support them in version 2.0. |
In OOo 1.1.0, default values are not supported. Default values are scheduled to be supported by version 2.0. This allows a default value to be specified if an optional argument is missing. You must use the keywords "Option Compatible" for default values to work.
Option Compatible Sub DefaultExample(Optional n as Integer=100) REM If IsMissing(n) Then n = 100 'I will not have to do this anymore! Print n End Sub
A recursive routine calls itself. As of OpenOffice.org 1.1, recursion is supported. Consider, for example, calculating the mathematical function Factorial for positive integers. The usual definition is recursive (see Listing 28 ).
Sub DoFactorial Print "Recursive Factorial = " & RecursiveFactorial(4) Print "Iterative Factorial = " & IterativeFactorial(4) End Sub Function IterativeFactorial(ByVal n As Long) As Long Dim answer As Long answer = 1 Do While n > 1 answer = answer * n n = n - 1 Loop IterativeFactorial = answer End Function ' This finally works in version 1.1 Function RecursiveFactorial(ByVal n As Long) As Long RecursiveFactorial = 1 If n > 1 Then RecursiveFactorial = n * RecursiveFactorial(n-1) End Function
Computers use a data structure called a stack. At home, I have a stack of books that I want to read. When I receive a new book, I place it on top of the stack. When I have time to read, I take the top book from the stack. This is similar to the data structure that a computer uses: a section of memory in a computer for temporary storage in which the last item stored is the first retrieved. Stacks are usually used when a computer calls a routine and passes arguments. A typical procedure follows:
Push the current run location onto the stack.
Push each argument onto the stack.
Call the desired function or subroutine.
The called routine uses the arguments from the stack.
The called routine frequently uses the stack to store its own variables.
The called routine removes the arguments from the stack.
The called routine removes and saves the caller's location from the stack.
If the called routine is a function, the return value is placed on the stack.
The called routine returns to the caller.
If the called routine is a function, the return value is taken from the stack.
Although various optimizations are used, there is always some overhead associated with calling subroutines and functions. There is overhead in running time and in the memory required. The recursive version of Factorial continually calls itself. While calculating the factorial of four, there is one point at which the stack contains information for calls for 4, 3, 2, and 1. For some functions-the Fibonacci series, for example-this call behavior may be prohibitive, and a non-recursive algorithm should be used instead.