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
Maths
Mid
Minute
Month
MonthName
MsgBox
Now
Oct
Replace
RGB
Right
Rnd
Round
ScriptEngine
ScriptEngineBuildVersion
ScriptEngineMajorVersion
ScriptEngineMinorVersion
Second
SetLocale
Sgn
Sin
Space
Split
Sqr
StrComp
String
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:

  début
        lire les valeurs et les mettre dans un tableau T
        trier le tableau T
        écrire les valeurs triées du tableau T à l'écran
  fin

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:

   début
         lire_tableau(T)
         trier_tableau(T)
         écrire_tableau(T)
   fin

where T represents an array. The operations

    . lire_tableau(T)
    . trier_tableau(T)
    . écrire_tableau(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:


début
    écrire "Tapez la suite de valeurs à trier sous la forme val1 val2 ... : "
    lire valeurs
    construire tableau T à partir de la chaîne valeurs
fin

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:


début
    construire chaîne texte "valeur1,valeur2,...." à partir du tableau T
    écrire texte
fin

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

début
      N<-- indice dernier élément du tableau T
      pour IFIN variant de N à 1
      faire
          //on recherche l'indice IMAX du plus gd élément de T
          // IFIN est l'indice du dernier élément de T

          chercher_max(T, IFIN, IMAX) 

          // on échange l'élément le plus grand de T avec le dernier élément de T

          échanger (T, IMAX, IFIN)

      finfaire
FIN

Here, the algorithm again uses non-elementary actions:


 . chercher_max(T, IFIN, IMAX)
 . échanger(T, IMAX, IFIN)

find_max(T, IFIN, IMAX) returns the index IMAX of the largest element in 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
        finfaire
    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 nomProcédure([Byref/Byval] param1, [Byref/Byval] param2, ...)
    instructions
end sub

and that of a function

function nomFonction([Byref/Byval] param1, [Byref/Byval] param2, ...)
    instructions
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 nomProcédure([Byref/Byval] param1, [Byref/Byval] param2, ...)

function nomFonction([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 nomFonction([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:

résultat=nomFonction(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 mode (byVal)

When a formal parameter specifies this mode of passing, 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 method 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 effective parameter and the formal parameter are then different
  ' j is passed by value (byref) - the effective parameter and the formal parameter are then identical
  ' the k passage mode is not specified. The default is by reference
  i=i+1
  j=j+1
  k=k+1
  affiche "dans proc1",i,j,k
End Sub
 
 Sub affiche(byval msg, ByVal i, ByVal j, ByVal k)
  ' displays the values of i and j and k
  wscript.echo msg & " i=" & i & " j=" & j & " k=" & k
End Sub
 
 
' ------------- calls to functions and procedures
 
' init i and j
  i=4:j=5 : k=6
 
' check
  affiche "dans programme principal, avant l'appel à proc1 :",i,j,k

' call proc1 procedure
  proc1 i,j,k
 
' check
  affiche "dans programme principal, après l'appel à proc1 :",i,j,k
 
' end
  wscript.quit 0

Results

dans programme principal, avant l'appel à proc1 : i=4 j=5 k=6
dans proc1 i=5 j=6 k=7
dans programme principal, après l'appel à 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
résultat=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 function used as a procedure
  • parentheses around the parameters of a function

5.3.4. Some examples of functions

Below are some examples of function definitions and uses:

Program


 Function plusgrandque(byval i, ByVal j)
  ' returns boolean true if i>j, boolean false otherwise
 
  ' data verification
  If isnumeric(i) And isnumeric(j) Then
    If i>j Then
      plusgrandque=true
    Else
      plusgrandque=false
    End If
  Else
    wscript.echo "Arguments (" & i & "," & j & ") erronés"
    plusgrandque=false
  End If
  Exit Function
End Function
 
 Function rendUnTableau(byval n)
  ' makes an array of n elements
  tableau=array()
  ' check validity of parameter n
  If isnumeric(n) And n>=1 Then
    ReDim Preserve tableau(n)
    For i= 0 To n-1
      tableau(i)=i
    Next
  Else
    wscript.echo "Argument [" & n & "] erroné"
  End If
  ' we return the result
  rendUnTableau=tableau
End Function
 
 Function argumentsVariables(byref arguments)
  ' arguments is an array of numbers whose sum is returned
  somme=0
  For i=0 To ubound(arguments)
    somme=somme+arguments(i)
  Next
  argumentsVariables=somme
End Function
 
  ' two parameter-free functions declared in 2 different ways
  Function sansParametres1
    sansParametres=4
  End Function
 
  Function sansParametres2()
    sansParametres=4
  End Function
 

' ------------- calls to functions and procedures
 
' calls function plusgrandque
  wscript.echo "plusgrandque(10,6)=" & plusgrandque(10,6)
  wscript.echo "plusgrandque(6,10)=" & plusgrandque(6,10)
  wscript.echo "plusgrandque(6,6)=" & plusgrandque(6,6)
  wscript.echo "plusgrandque(6,'a')=" & plusgrandque(6,"a")
 
' calls to the rendUnTableau function
  monTableau=rendUnTableau(10)
  For i=0 To ubound(monTableau)
    wscript.echo monTableau(i)
  Next
  monTableau=rendUnTableau(-6)
  For i=0 To ubound(monTableau)
    wscript.echo monTableau(i)
  Next
 
' calls to the argumentsVariables function
  wscript.echo "somme=" & argumentsVariables(array(-1,2,7,8))
  wscript.echo "somme=" & argumentsVariables(array(-1,10,12))
 
' function calls without parameters
  res=sansParametres1
  res=sansParametres1()
  sansParametres1
  sansParametres1()
 
  res=sansParametres2
  res=sansParametres2()
  sansParametres2
  sansParametres2()
 
' end
  wscript.quit 0

Results

plusgrandque(10,6)=Vrai
plusgrandque(6,10)=Faux
plusgrandque(6,6)=Faux
Arguments (6,a) erronés
plusgrandque(6,'a')=Faux
0
1
2
3
4
5
6
7
8
9

Argument [-6] erroné
somme=16
somme=21
somme=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. There is then 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 table of values to be sorted

' reading values
T=lire_tableau

' value sorting
trier_tableau T

' display of sorted values
ecrire_tableau T

' end
wscript.quit 0

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

' -------- lire_tableau
Function lire_tableau
    ' values are requested
    wscript.stdout.write "Tapez les valeurs à trier sous la forme val1 val2  ... valn : "
    ' we read them
    Dim valeurs
    valeurs=wscript.stdin.readLine
    ' we put them in a table
    lire_tableau=split(valeurs," ")
End Function

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

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

    ' find the imax index of the T[0..ifin] array
    ' to exchange T[imax] with the last element of array T[0..ifin]
    ' then start again with an array with 1 fewer element

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

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

    ' We return the result
    chercher_max=imax
End Function

Results

Tapez les valeurs à trier sous la forme 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 program in modular form

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

Program

' calculating a taxpayer's tax liability
' the program must be called with three parameters: married children salary
' married: character Y if married, N if unmarried
' children: number of children
' salary: annual salary without cents

' mandatory variable declaration
Option Explicit
Dim erreur

' retrieve arguments and check their validity
Dim marie, enfants, salaire
erreur=getArguments(marie,enfants,salaire)
' mistake?
If erreur(0)<>0 Then wscript.echo erreur(1) : wscript.quit erreur(0)

' retrieve the data needed to calculate taxes
Dim limites, coeffR, coeffN
getData limites,coeffR,coeffN

' the result is displayed
wscript.echo "impôt=" & calculerImpot(marie,enfants,salaire,limites,coeffR,coeffN)

' leave without error
wscript.quit 0

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

' ----------- getArguments
Function getArguments(byref marie, ByRef enfants, ByRef salaire)
    ' must retrieve three values passed as arguments to the main program
    ' an argument is passed to the program without spaces in front and behind it
    ' use regular expressions to check data validity

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

    Dim syntaxe
    syntaxe= _
    "Syntaxe : pg marié enfants salaire" & vbCRLF & _
    "marié : caractère O si marié, N si non marié" & vbCRLF & _
    "enfants : nombre d'enfants (entier >=0)" & vbCRLF & _
    "salaire : salaire annuel sans les centimes (entier >=0)"

    ' we check that there are 3 arguments
    Dim nbArguments
    nbArguments=wscript.arguments.count
    If nbArguments<>3 Then
        ' error msg
        getArguments= array(1,syntaxe & vbCRLF & vbCRLF & "erreur : nombre d'arguments incorrect")
        ' end
        Exit Function
    End If

    Dim modele, correspondances
    Set modele=new regexp

    ' marital status must be among the characters oOnN
    modele.pattern="^[oOnN]$"
    Set correspondances=modele.execute(wscript.arguments(0))
    If correspondances.count=0 Then
        ' error msg
        getArguments=array(2,syntaxe & vbCRLF & vbCRLF & "erreur : argument marie incorrect")
        ' we leave
        Exit Function
    End If
    ' the value
    If lcase(wscript.arguments(0)) = "o"Then
        marie=true
    Else
        marie=false
    End If

    ' children must be an integer >=0
    modele.pattern="^\d{1,2}$"
    Set correspondances=modele.execute(wscript.arguments(1))
    If correspondances.count=0 Then
        ' error
        getArguments= array(3,syntaxe & vbCRLF & vbCRLF & "erreur : argument enfants incorrect")
        ' we leave
        Exit Function
    End If
    ' the value
    enfants=cint(wscript.arguments(1))

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

    ' finished without error
    getArguments=array(0,"")
End Function

' ----------- getData
Sub getData(byref limites, ByRef coeffR, ByRef coeffN)
    ' we define the data needed to calculate the tax in 3 tables
    limites=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

' ----------- calculerImpot
Function calculerImpot(byval marie,ByVal enfants,ByVal salaire, ByRef limites, ByRef coeffR, ByRef coeffN)

    ' the number of shares is calculated
    Dim nbParts
    If marie=true Then
        nbParts=(enfants/2)+2
    Else
        nbParts=(enfants/2)+1
    End If
    If enfants>=3 Then nbParts=nbParts+0.5

    ' we calculate the family quotient and taxable income
    Dim revenu, qf
    revenu=0.72*salaire
    qf=revenu/nbParts

    ' tax calculation
    Dim i, impot
    i=0
    Do While i<ubound(limites) And qf>limites(i)
        i=i+1
    Loop
    calculerImpot=int(revenu*coeffr(i)-nbParts*coeffn(i))
End Function

Comments

  • The getArguments function retrieves the taxpayer's information (spouse, children, salary). Here, they are 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.