Skip to content

5. Functions and Procedures

5.1. VBScript's predefined functions

The richness of a language derives largely from its function library, as these functions can be encapsulated in objects under the name of methods. In this regard, VBScript can be considered rather limited.

The following table lists VBScript functions outside of objects. We will not go into detail about them. Their names generally indicate their purpose. Readers should consult the documentation for details on a specific function.

Abs
Array
Asc
Atn
CBool
CByte
CCur
CDate
CDbl
Chr
CInt
CLng
Conversions
Cos
CreateObject
CSng
Date
DateAdd
DateDiff
DatePart
DateSerial
DateValue
Day
Derived Maths
Eval
Exp
Filter
Currency Format
FormatDateTime
Number Format
Percentage Format
GetLocale
GetObject
GetRef
Hex
Hour
InputBox
InStr
InStrRev
Int, Fixs
IsArray
IsDate
IsEmpty
IsNull
IsNumeric
IsObject
Join
LBound
LCase
Left
Len
LoadPicture
Log
LTrim; RTrim; and Trims
Math
Mid
Minute
Month
MonthName
MsgBox
Now
Oct
Replace
RGB
Right
Rnd
Round
ScriptEngine
ScriptEngineBuildVersion
ScriptEngineMajorVersion
ScriptEngineMinorVersion
Second
SetLocale
Sgn
Sin
Space
Split
Sqr
StrComp
Thong
Tan
Time
Timer
TimeSerial
TimeValue
TypeName
UBound
UCase
VarType
Weekday
WeekdayName
Year

5.2. Modular Programming

Describing the programmed solution to a problem means describing the sequence of basic operations that can be executed by the computer and are capable of solving the problem. Depending on the programming language, these basic operations vary in complexity. Examples include:

  • reading data from the keyboard or disk
  • writing data to the screen, a printer, a disk, etc.
  • calculating expressions
  • navigating through a file
  • ...

Describing a complex problem may require several thousand of these basic instructions or more. It is therefore very difficult for the human mind to grasp the big picture of a program. Faced with this difficulty in grasping the problem as a whole, we break it down into subproblems that are easier to solve. Consider the following problem: Sort a list of numerical values entered via the keyboard and display the sorted list on the screen.

We can initially describe the solution in the following form:

  start
        read the values and put them into an array T
        sort the array T
        write the sorted values from array T to the screen
  end

We have broken the problem down into 3 subproblems, which are easier to solve. The algorithmic notation is often more formalized than the previous one, and the algorithm would be written as follows:


   begin
         read_array(T)
         sort_array(T)
         write_array(T)
   end

where T represents an array. The operations

    . read_array(T)
    . sort_array(T)
    . write_array(T)

are non-elementary operations that must in turn be described by elementary operations. This is done in what are called modules. The data T is called a module parameter. It is information that the calling program passes to the called module (input parameter) or receives from the called module (output parameter). The parameters of a module are therefore the information exchanged between the calling program and the called module.

module read_array(T)
module sort_array(T)
module read_array(T)

The module read_array(T) could be described as follows:


begin
    print "Enter the sequence of values to sort in the form val1 val2 ... : "
    read values
    construct array T from the string values
end

Here, we have sufficiently described the read_array module. Indeed, the three necessary actions have an immediate translation into VBScript. The last one will require the use of the Split function. If VBScript did not have this function, action 3 would in turn have to be broken down into elementary actions having an immediate equivalent in VBScript.

The write_array(T) module could be described as follows:


start
    construct text string "value1,value2,...." from array T
    write text
end

The write_array(T) module could be described as follows (assuming that the indices of the elements of T start at 0):

start
      N<-- index of the last element of array T
      for IFIN ranging from N to 1
      do
          //find the index IMAX of the largest element in T
          // IFIN is the index of the last element of T

          find_max(T, IFIN, IMAX) 

          // swap the largest element of T with the last element of T

          swap(T, IMAX, IFIN)

      end
END

Here, the algorithm again uses non-elementary actions:


 . find_max(T, IFIN, IMAX)
 . swap(T, IMAX, IFIN)

find_max(T, IFIN, IMAX) returns the index IMAX of the largest element in the array T whose last element has index IFIN.

swap(T, IMAX, IFIN) swaps two elements of the array T, those at indices IMAX and IFIN.

We must therefore describe the new non-elementary operations.

module find_max(A, IFIN, IMAX)
   begin
        IMAX<--0

        for i ranging from 1 to IFIN
        do
           if T[i] > T[IMAX] then
             start
                IMAX<--i
             end
        end do
    end
exchange module(T IMAX, IFIN)
  start
       temp<----T[IMAX]
       T[IMAX]<---T[IFIN]
       T[IFIN]<---temp
  end

The initial problem has been fully described using basic VBScript operations and can therefore now be translated into this language. Note that basic operations may differ from one language to another, and therefore the analysis of a problem must at some point take into account the programming language being used. An object that exists in one language may not exist in another, thereby altering the algorithm used. Thus, if a language had a sorting function, it would be absurd not to use it here.

The principle applied here is known as top-down analysis. If we outline the framework of the solution, we have the following:

We have a tree structure.

5.3. VBScript Functions and Procedures

Once the modular analysis has been performed, the programmer can translate the modules of their algorithm into VBScript functions or procedures. Both functions and procedures accept input/output parameters, but a function returns a result that allows it to be used in expressions, whereas a procedure does not.

5.3.1. Declaration of VBScript functions and procedures

The declaration of a VBScript procedure is as follows

sub ProcedureName([Byref/Byval] param1, [Byref/Byval] param2, ...)
    statements
end sub

and that of a function

function functionName([Byref/Byval] param1, [Byref/Byval] param2, ...)
    statements
end sub

To return its result, the function must include an assignment statement that assigns the result to a variable with the same name as the function:

functionName=result

The execution of a function or procedure ends in two ways:

  1. upon encountering the end function or end sub statement
  2. upon encountering the exit function or exit sub statement

For a function, note that the result must have been assigned to a variable with the function’s name before the function ends with an end function or exit function.

5.3.2. Parameter Passing Modes for a Function or Procedure

In the declaration of the input-output parameters of a function or procedure, the mode (byRef, byVal) of parameter passing from the calling program to the called program is specified:

sub procedureName([Byref/Byval] param1, [Byref/Byval] param2, ...)

function functionName([Byref/Byval] param1, [Byref/Byval] param2, ...)

When the byRef or byVal mode is not specified, the byRef mode is used.

Actual parameters, formal parameters

Consider a VBScript function defined as

function functionName([ByRef/ByVal] paramForm1, [ByRef/ByVal] paramForm2, ...)
...
end function

The parameters paramForm1 used in the definition of the function or procedure are called formal parameters. The preceding function can be called from the main program or another module using a statement such as:

result = FunctionName(paramEff1, paramEff2, ...)

The parameters paramEffi used in the call to the function or procedure are called actual parameters. When the execution of the function functionName begins, the formal parameters receive the values of the corresponding actual parameters. The keywords byRef and byVal determine the mode of transmission for these values.

Pass-by-value (byVal)

When a formal parameter specifies this passing mode, the formal parameter and the actual parameter are two different variables. The value of the actual parameter is copied into the formal parameter before the function or procedure is executed. If the function or procedure modifies the value of the formal parameter during execution, this does not affect the value of the corresponding actual parameter. This passing mode is well-suited for the input parameters of a function or procedure.

Pass-by-reference mode (byRef)

This passing mode is the default if no parameter passing mode is specified. When a formal parameter specifies this passing mode, the formal parameter and the corresponding actual parameter are one and the same variable. Thus, if the function modifies the formal parameter, the actual parameter is also modified. This passing mode is well-suited for:

  • output parameters, since their values must be passed back to the calling program
  • input parameters that are costly to copy, such as arrays

The following program shows examples of parameter passing:

Program

Sub proc1(byval i, ByRef j, k)
  ' i is passed by value (byval) - the actual parameter and the formal parameter are therefore different
  ' j is passed by value (byval) - the actual parameter and the formal parameter are then identical
  ' the passing mode for k is not specified. By default, it is by reference
  i = i + 1
  j = j + 1
  k=k+1
  display "in proc1", i, j, k
End Sub

 Sub display(ByVal msg, ByVal i, ByVal j, ByVal k)
  ' displays the values of i, j, and k
  wscript.echo msg & " i=" & i & " j=" & j & " k=" & k
End Sub


' ------------- calls to functions and procedures

' initialize i and j
  i=4:j=5 : k=6

' verification
  display "in main program, before the call to proc1:", i, j, k

' call procedure proc1
  proc1 i,j,k

' verification
  print "in main program, after the call to proc1:", i, j, k

' end
  wscript.quit 0
Results
in main program, before the call to proc1: i=4 j=5 k=6
in proc1 i=5 j=6 k=7
in main program, after calling proc1: i=4 j=6 k=7

Comments

  • In a VBScript script, there is no specific place for functions and procedures. They can be anywhere in the source code. Generally, they are grouped either at the beginning or at the end, and the main program is structured as a continuous block.

5.3.3. Syntax for calling functions and procedures

Let p be a procedure accepting formal parameters pf1, pf2, ...

  • the call to procedure p takes the form
p pe1, pe2, ...

without parentheses around the parameters

  • if procedure p accepts no parameters, one may use either the call p or p() and the declaration sub p or sub p()

Let f be a function that accepts formal parameters pf1, pf2, ...

  • The function f is called in the form
result=f(pe1, pe2, ...)

Parentheses around the parameters are mandatory. If the function f takes no parameters, one can use either the call f or f() and the declaration function f or function f().

  • The result of function f may be ignored by the calling program. Function f is then considered a procedure and follows the rules for calling procedures. We then write f pe1, pe2, ... (without parentheses) to call function f.

If the function or procedure is an object method, the rules appear to be somewhat different and inconsistent.

  • Thus, we can write MyFile.WriteLine "This is a test." or MyFile.WriteLine("This is a test.")
  • but while we can write wscript.echo 4, we cannot write wscript.echo(4).

We will adhere to the following rules:

  • no parentheses around the parameters of a procedure or a function used as a procedure
  • parentheses around the parameters of a function

5.3.4. Some examples of functions

Below are a few examples of function definitions and uses:

Program

 Function greaterThan(ByVal i, ByVal j)
  ' returns true if i > j, false otherwise

  ' Data validation
  If IsNumeric(i) And IsNumeric(j) Then
    If i > j Then
      greaterthan=true
    Else
      greaterThan=false
    End If
  Else
    wscript.echo "Invalid arguments (" & i & "," & j & ")"
    greaterThan=false
  End If
  Exit Function
End Function

 Function returnAnArray(byval n)
  ' returns an array of n elements
  array = array()
  ' Check validity of parameter n
  If isnumeric(n) And n>=1 Then
    ReDim Preserve array(n)
    For i = 0 To n-1
      array(i) = i
    Next
  Else
    wscript.echo "Argument [" & n & "] is incorrect"
  End If
  ' Return the result
  returnArray = array
End Function

 Function argumentsVariables(byref arguments)
  ' arguments is an array of numbers whose sum is returned
  sum = 0
  For i = 0 To UBound(arguments)
    sum = sum + arguments(i)
  Next
  argumentsVariables = sum
End Function

  ' Two parameterless functions declared in two different ways
  Function noParameters1
    noParameters=4
  End Function

  Function noParameters2()
    noParameters=4
  End Function


' ------------- calls to functions and procedures

' calls to the greaterThan function
  wscript.echo "greaterThan(10,6)=" & greaterThan(10,6)
  wscript.echo "greaterThan(6,10)=" & greaterThan(6,10)
  wscript.echo "greaterThan(6,6)=" & greaterThan(6,6)
  wscript.echo "greaterThan(6,'a')=" & greaterThan(6,"a")

' calls to the rendUnTableau function
  myArray = rendUnTableau(10)
  For i=0 To ubound(myArray)
    wscript.echo myArray(i)
  Next
  myArray = rendUnTableau(-6)
  For i = 0 To ubound(myArray)
    wscript.echo myArray(i)
  Next

' calls to the argumentsVariables function
  wscript.echo "sum=" & argumentsVariables(array(-1,2,7,8))
  wscript.echo "sum=" & argumentsVariables(array(-1,10,12))

' calls to functions without parameters
  res=noParameters1
  res = noParameters1()
  noParameters1
  noParameters1()

  res = noParameters2
  res = noParameters2()
  noParameters2
  noParameters2()

' end
  wscript.quit 0
Results
greaterThan(10,6)=True
greaterThan(6,10)=False
greaterthan(6,6)=False
Invalid arguments (6,a)
greaterthan(6,'a')=False
0
1
2
3
4
5
6
7
8
9

Invalid argument [-6]
sum=16
sum=21
sum=10

Comments

  • The `rendUnTableau` function demonstrates that a function can return multiple results rather than just one. It simply needs to place them in an array variant and return that variant as the result.
  • Conversely, the `argumentsVariables` function demonstrates that you can write a function that accepts a variable number of arguments. Here, too, you simply need to place them in an array variant and make this variant a function parameter.

5.3.5. Output parameter or function result

Suppose that the analysis of an application has shown the need for a module M with input parameters Ei and output parameters Sj. Recall that input parameters are information that the calling program provides to the called program, and conversely, output parameters are information that the called program provides to the calling program. In VBScript, there are several solutions for output parameters:

  • If there is only one output parameter, it can be made the result of a function. In this case, there is no longer an output parameter, but simply a function result.
  • If there are n output parameters, one of them can serve as the function result, with the remaining n-1 acting as output parameters. Alternatively, instead of a function, one can use a procedure with n output parameters. One can also use a function that returns an array containing the n values to be returned to the calling program. Recall that the called program returns its results to the calling program by value. This copying is avoided when output parameters are passed by reference. This latter solution therefore saves time.

5.4. The VBScript program for sorting values

We began our discussion of modular programming with an algorithmic study of sorting numerical values entered via the keyboard. Here is the VBScript implementation that could be used:

Program
' main program
Option Explicit

Dim T        ' the array of values to be sorted

' read values
T = read_array

' Sort the values
sort_array T

' display the sorted values
print_array T

' end
wscript.quit 0

' ---------- functions & procedures

' -------- read_array
Function read_array
    ' prompt for values
    wscript.stdout.write "Enter the values to be sorted in the form val1 val2  ... valn: "
    ' read them
    Dim values
    values = wscript.stdin.readLine
    ' we put them into an array
    read_array = Split(values, " ")
End Function

' -------- write_array
Sub write_array(byref T)
    ' displays the contents of array T
    wscript.echo join(T, " ")
End Sub

' -------- sort_array
Sub sort_array (byref T)
    ' sorts the array T in ascending order

    ' find the index imax of the array T[0..ifin]
    ' to swap T[imax] with the last element of the array T[0..ifin]
    ' then we start over with an array that has one fewer element

    Dim ifin, imax, temp
    For ifin = ubound(T) To 1 Step -1
        ' Find the index imax of the array T[0..ifin]
        imax = find_max(T, ifin)
        ' swap max with the last element of the array T[0..ifin]
        temp = T(ifin):T(ifin) = T(imax):T(imax) = temp
    Next
End Sub

' -------- find_max
Function find_max(byRef T, ByVal ifin)
    ' Find the index imax in the array T[0..ifin]
    Dim i, imax
    imax = 0
    For i = 1 To ifin
        If cdbl(T(i)) > cdbl(T(imax)) Then imax = i
    Next

    ' Return the result
    find_max = imax
End Function
Results
Enter the values to be sorted in the form val1 val2  ... valn: 10 9 8 7 6 1
1 6 7 8 9 10

Comments:

  • The swap module identified in the initial algorithm was not implemented as a VBScript module here because it was deemed too simple to warrant a separate module.

5.5. The Tax Calculation program in modular form

We revisit the tax calculation program, this time written in a modular form

Program
' Calculation of a taxpayer's tax
' The program must be called with three parameters: married children salary
' married: character O if married, N if unmarried
' children: number of children
' salary: annual salary without cents

' mandatory variable declaration
Option Explicit
Dim error

' retrieve the arguments and check their validity
Dim wife, children, salary
error = GetArguments(wife, children, salary)
' error?
If error(0) <> 0 Then wscript.echo error(1) : wscript.quit error(0)

' Retrieve the data needed to calculate the tax
Dim limits, coeffR, coeffN
getData limits, coeffR, coeffN

' display the result
wscript.echo "tax=" & calculateTax(spouse,children,salary,limits,coeffR,coeffN)

' exit without error
wscript.quit 0

' ------------ functions and procedures

' ----------- getArguments
Function getArguments(ByRef wife, ByRef children, ByRef salary)
    ' must retrieve three values passed as arguments to the main program
    ' an argument is passed to the program without spaces before or after
    ' regular expressions will be used to verify the validity of the data

    ' returns an error array variant with 2 values
    ' error(0): error code, 0 if no error
    ' error(1): error message if an error occurs, otherwise an empty string

    Dim syntax
    syntax = _
    "Syntax: pg married children salary" & vbCRLF & _
    "married: character O if married, N if unmarried" & vbCRLF & _
    "children: number of children (integer >=0)" & vbCRLF & _
    "salary: annual salary without cents (integer >=0)"

    ' Check that there are 3 arguments
    Dim nbArguments
    nbArguments = wscript.arguments.count
    If nbArguments<>3 Then
        ' error message
        getArguments = array(1, "syntax" & vbCRLF & vbCRLF & "error: incorrect number of arguments")
        ' end
        Exit Function
    End If

    Dim pattern, matches
    Set pattern = new regexp

    ' the marital status must be one of the characters oOnN
    pattern.pattern="^[oOnN]$"
    Set matches = template.execute(wscript.arguments(0))
    If matches.count = 0 Then
        ' error message
        getArguments = Array(2, "syntax", & vbCRLF, & vbCRLF, & "error: incorrect argument")
        ' exit
        Exit Function
    End If
    ' retrieve the value
    If lcase(wscript.arguments(0)) = "o" Then
        marie = true
    Else
        marie = false
    End If

    ' children must be an integer >= 0
    pattern.pattern="^\d{1,2}$"
    Set matches = pattern.execute(wscript.arguments(1))
    If matches.count=0 Then
        ' error
        getArguments = array(3, syntax & vbCRLF & vbCRLF & "error: incorrect children argument")
        ' exit
        Exit Function
    End If
    ' retrieve the value
    children = Int(WScript.Arguments(1))

    ' salary must be an integer >=0
    pattern.pattern="^\d{1,9}$"
    Set matches = pattern.execute(wscript.arguments(2))
    If matches.count=0 Then
        ' error
        getArguments = array(4, syntax & vbCRLF & vbCRLF & "error: incorrect salary argument")
        ' exit
        Exit Function
    End If
    ' retrieve the value
    salary = clng(wscript.arguments(2))

    ' it's done without errors
    getArguments = array(0, "")
End Function

' ----------- getData
Sub getData(ByRef limits, ByRef coeffR, ByRef coeffN)
    ' We define the data needed to calculate the tax in 3 arrays
    limits = array(12620, 13190, 15640, 24740, 31810, 39970, 48360, _
    55790,92970,127860,151250,172040,195000,0)
    coeffr=array(0,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45, _
    0.5,0.55,0.6,0.65)
    coeffn=array(0,631,1290.5,2072.5,3309.5,4900,6898.5,9316.5, _
    12106,16754.5,23147.5,30710,39312,49062)
End Sub

' ----------- calculateTax
Function calculateTax(ByVal spouse, ByVal children, ByVal salary, ByRef limits, ByRef coeffR, ByRef coeffN)

    ' calculate the number of brackets
    Dim nbParts
    If spouse=true Then
        nbParts = (children / 2) + 2
    Else
        nbParts = (children / 2) + 1
    End If
    If children >= 3 Then nbParts = nbParts + 0.5

    ' Calculate the family quotient and taxable income
    Dim income, qf
    income = 0.72 * salary
    qf = income / nbParts

    ' Calculate the tax
    Dim i, tax
    i = 0
    Do While i < ubound(limits) And qf > limits(i)
        i = i + 1
    Loop
    calculateTax = INT(income * coeffr(i) - nbParts * coeffn(i))
End Function

Comments

  • The getArguments function retrieves the taxpayer's information (spouse, children, income). Here, this information is passed as arguments to the VBScript program. If this were to change—for example, if these arguments came from a graphical user interface—only the getArguments procedure would need to be rewritten, not the others.
  • The getArguments function can detect errors in the arguments. When this happens, one could have decided to stop the program’s execution within the getArguments function using a wscript.quit statement. This should never be done within a function or procedure. If a function or procedure detects an error, it must signal this in some way to the calling program. It is up to the calling program to decide whether to stop execution or not, not the procedure. In our example, the calling program might decide to ask the user to re-enter the incorrect data via the keyboard rather than stopping execution.
  • Here, the getArguments function returns an array variant where the first element is an error code (0 if no error) and the second is an error message if an error occurred. By checking the returned result, the calling program can determine whether an error occurred or not.
  • The getData procedure retrieves the data needed to calculate the tax. Here, this data is defined directly within the getData procedure. If this data were to come from another source—such as a file or a database—only the getData procedure would need to be rewritten, not the others.
  • The `calculerImpot` function calculates the tax once all the data has been obtained, regardless of how it was obtained.
  • Note, therefore, that modular coding allows for the (re)use of certain modules in different contexts. This concept has been heavily developed over the past twenty years within the concept of objects.