• home
  • forum
  • my
  • kt
  • download
  • Procedures - Functions and Subroutines

    Author: 2007-09-06 10:29:56 From:

    This chapter describes:

    • What Is a Procedure?
    • Defining and Invoking Function Procedures
    • Function Procedure Example
    • Defining and Invoking Sub Procedures
    • Sub Procedure Example
    • Rules of Passing Arguments
    • Example - Passing Arguments by Reference
    • Example - Passing Arguments by Value
    • Passing Array as Arguments
    • Variable Scope in Procedures
    • Example - Variable Scopes

    Notes and samples in this chapter are based Visual Basic 6.0.

    What Is a Procedure?

    A Procedure is a unit of code outside of the main execution code. But it can be executed by an invoking statement in the main execution code. There are 3 aspects about procedures:

    1. Defining a procedure.

    2. Invoking a procedure.

    3. Exchanging data between the main execution code and a procedure.

    VB offers two types of procedures:

    1. Function Procedure - A procedure that returns a value explicitly.

    2. Sub Procedure - A procedure that does not return any value explicitly.

    Defining and Invoking Function Procedures

    A "Function" statement defines a function procedure with the following syntax:

       Function function_name(argument_list)
          statement_block
          function_name = return_value
       End Function
    

    where "function_name" is the name of the function, "argument_list" a list of variables used to pass data into and/or out of the function, and "return_value" is the value to be returned explicitly to the invoking statements.

    Of course, "argument_list" is optional.

    Assigning the return value to the function name is also optional. If not given, default value will be returned to the invoking statements. But this is not recommended.

    Invoking a function procedure is simple, no need of any special statements. Just use the function name with an argument list in any expression:

       ... function_name(argument_list) ...
    

    This will cause the system to:

    • Stop evaluating the expression.
    • Map data or variables based on the argument list.
    • Execute the entire statement block defined inside the function.
    • Take the value returned in the function name.
    • Continue to evaluate the expression.

    If you want terminate a function procedure early, you can use the "Exit" statement:

       Exit Function
    

    Function Procedure Example

    To help you understand the concept of function procedure, I wrote the following the example, function_f2c.html:

    <html>
    <body>
    <!-- function_f2c.html
       Copyright (c) 2006 by Dr. Herong Yang. http://www.herongyang.com/
    -->
    <pre>
    <script language="vbscript">
       d = F2C(70.0)
       document.writeln("Received Celsius = " & d)
       d = F2C(212.0)
       document.writeln("Received Celsius = " & d)
    
    Function F2C(dFahrenheit)
       document.writeln("")
       document.writeln("Converting Fahrenheit = " & dFahrenheit)
       dCelsius = (dFahrenheit - 32.0 ) / 1.8
       document.writeln("Returning Celsius = " & dCelsius)
       F2C = dCelsius
    End Function
    </script>
    </pre>
    </body>
    </html>
    

    Here is the output:

    Converting Fahrenheit = 70
    Returning Celsius = 21.1111111111111
    Received Celsius = 21.1111111111111
    
    Converting Fahrenheit = 212
    Returning Celsius = 100
    Received Celsius = 100
    

    Easy to understand. Right?

    Defining and Invoking Sub Procedures

    A Sub Procedure is similar to a function procedure. It can be defined with the "Sub" statement:

       Sub sub_name(argument_list)
          statement_block
       End Sub
    

    where "sub_name" is the name of the sub procedure (subroutine), and "argument_list" a list of variables used to pass data into and/or out of the subroutine.

    Of course, "argument_list" is optional.

    Notice that subroutine does not return any values.

    Invoking a subroutine is different than a function procedure. You can use one of the two syntaxes below:

    1. Explicit call with "Call" statement:

       Call sub_name(argument_list)
    

    2. Explicit call with subroutine name:

       sub_name argument_list
    

    Both syntaxes will cause the system to:

    • Stop execution in main code flow.
    • Map data or variables based on the argument list.
    • Execute the entire statement block defined inside the subroutine.
    • Continue to execute main code flow.

    If you want terminate a sub procedure early, you can use the "Exit" statement:

       Exit Sub
    

    Sub Procedure Example

    Here is a short example of subroutines, function_sub.html:

    <html>
    <body>
    <!-- function_sub.html
       Copyright (c) 2006 by Dr. Herong Yang. http://www.herongyang.com/
    -->
    <pre>
    <script language="vbscript">
       Call Hello("Tom")
       Hello "Herong"
    
    Sub Hello(sName)
       document.writeln("")
       document.writeln("Helo " & sName)
    End Sub
    </script>
    </pre>
    </body>
    </html>
    

    Here is the output:

    Helo Tom
    
    Helo Herong
    

    Notice the two different ways of calling a subroutine.

    Rules of Passing Arguments

    As shown in previous examples, passing arguments to procedures seems to be a simple job. But it may cause confusion if you don't following the rules. VB script has the following rules on passing arguments to procedures:

    1. By default, arguments are passed by reference. In this case, an argument name can be used as a variable that referring (sharing) the same data as the calling code.

    2. But, arguments can be passed by value, if you put the key word "ByVal" before the argument name. In this case, an argument name can be used as a variable that contains a copy of the data provided by the calling code.

    3. Of course, you put the word "ByRef" before an argument name to declare a pass-by-reference argument explicitly.

    4. A pass-by-reference argument can be used to allow the procedure to alter data that is associated with a variable in the calling code. This allows the procedure to output data back to the calling code.

    5. A pass-by-value argument is safer than a pass-by-reference argument, because the procedure only gets a copy of the data. Any changes to that data will not affect the original data in the calling code.

    6. Arrays can be passed by reference.

    7. Arrays can also be passed by value. In this case, the procedure will get a copy of the array.

    8. I don't know how to specify an array as the return value of a function procedure.

    Example - Passing Arguments by Reference

    To see how passing arguments by reference works, I wrote the following example, function_swap_by_ref.html:

    <html>
    <body>
    <!-- function_swap_by_ref.html
       Copyright (c) 2006 by Dr. Herong Yang. http://www.herongyang.com/
    -->
    <pre>
    <script language="vbscript">
       document.writeln("")
       document.writeln("Test 1: Swapping two literals by reference")
       document.writeln("   Before Sub: " & "Apple" & " | " & "Orange")
       Call SwapByRef("Apple", "Orange")
       document.writeln("   After Sub: " & "Apple" & " | " & "Orange")
    
       vFirst = "Dog"
       vSecond = "Cat"
       document.writeln("")
       document.writeln("Test 2: Swapping two variables by reference")
       document.writeln("   Before Sub: " & vFirst & " | " & vSecond)
       Call SwapByRef(vFirst, vSecond)
       document.writeln("   After Sub: " & vFirst & " | " & vSecond)
    
    Sub SwapByRef(ByRef vLeft, ByRef vRight)
       vTemp = vLeft
       vLeft = vRight
       vRight = vTemp
       document.writeln("   In Sub: " & vLeft & " | " & vRight)
    End Sub
    </script>
    </pre>
    </body>
    </html>
    

    Here is the output:

    Test 1: Swapping two literals by reference
       Before Sub: Apple | Orange
       In Sub: Orange | Apple
       After Sub: Apple | Orange
    
    Test 2: Swapping two variables by reference
       Before Sub: Dog | Cat
       In Sub: Cat | Dog
       After Sub: Cat | Dog
    

    Here are my comments about this example:

    • Test 1 shows that data literal can be used for a "ByRef" argument. By you will not be able to receive the change done by the subroutine.
    • Test 2 shows that using variable for a "ByRef" argument lets to receive the change by the subroutine. After the subroutine call, values in vFirst and vSecond have been swapped.
    • "ByRef" keyword is optional.

    Example - Passing Arguments by Value

    To see how passing arguments by value works, I wrote the following example, function_swap_by_val.html:

    <html>
    <body>
    <!-- function_swap_by_val.html
       Copyright (c) 2006 by Dr. Herong Yang. http://www.herongyang.com/
    -->
    <pre>
    <script language="vbscript">
       document.writeln("")
       document.writeln("Test 1: Swapping two literals by value")
       document.writeln("   Before Sub: " & "Apple" & " | " & "Orange")
       Call SwapByVal("Apple", "Orange")
       document.writeln("   After Sub: " & "Apple" & " | " & "Orange")
    
       vFirst = "Dog"
       vSecond = "Cat"
       document.writeln("")
       document.writeln("Test 2: Swapping two variables by value")
       document.writeln("   Before Sub: " & vFirst & " | " & vSecond)
       Call SwapByVal(vFirst, vSecond)
       document.writeln("   After Sub: " & vFirst & " | " & vSecond)
    
    Sub SwapByVal(ByVal vLeft, ByVal vRight)
       vTemp = vLeft
       vLeft = vRight
       vRight = vTemp
       document.writeln("   In Sub: " & vLeft & " | " & vRight)
    End Sub
    </script>
    </pre>
    </body>
    </html>
    

    Here is the output:

    Test 1: Swapping two literals by value
       Before Sub: Apple | Orange
       In Sub: Orange | Apple
       After Sub: Apple | Orange
    
    Test 2: Swapping two variables by value
       Before Sub: Dog | Cat
       In Sub: Cat | Dog
       After Sub: Dog | Cat
    

    Here are my comments about this example:

    • Test 1 is useless.
    • Test 2 shows that "ByVel" arguments will not bring any changes back to the calling code. After the subroutine call, values in vFirst and vSecond have not been changed at all.

    Passing Array as Arguments

    As I mentioned earlier, arrays can also be passed as arguments. If an array is passed by reference, the procedure is working on the same array as the calling code. If an array is passed by value, the procedure is working on a independent copy of the array in the calling code.

    Here is an example code of using array as an argument, function_reverse_array.html:

    <html>
    <body>
    <!-- function_reverse_array.html
       Copyright (c) 2006 by Dr. Herong Yang. http://www.herongyang.com/
    -->
    <pre>
    <script language="vbscript">
       document.writeln("")
       document.writeln("Test 1: Reversing a data literal")
       bOk = ReverseArray("Apple")
    
       aPets = Array("Bird", "Cat", "Dog", "Fish", "Rabbit")
       document.writeln("")
       document.writeln("Test 2: Reversing an array")
       document.writeln("   Before Sub: " & Join(aPets))
       bOk = ReverseArray(aPets)
       document.writeln("   After Sub: " & Join(aPets))
    
    Function ReverseArray(ByRef aList)
       If IsArray(aList) Then
          iMin = LBound(aList)
          iMax = UBound(aList)
          For i=iMin to iMax\2
             j = iMax - (i-iMin) 
             vTemp = aList(i)
             aList(i) = aList(j)
             aList(j) = vTemp
          Next
          ReverseArray = True
       Else
          document.writeln("Error: You are not giving an array.")
          ReverseArray = False
       End If
    End Function
    </script>
    </pre>
    </body>
    </html>
    

    Here is the output:

    Test 1: Reversing a data literal
    Error: You are not giving an array.
    
    Test 2: Reversing an array
       Before Sub: Bird Cat Dog Fish Rabbit
       After Sub: Rabbit Fish Dog Cat Bird
    

    My "ReverseArray" function worked perfectly. You can take it a utility function to your application.

    Variable Scope in Procedures

    Variable Scope - The area of source code where a variable is accessible.

    If you are not using procedures, variable scope is very simple. The scope of a variable is: from the statement where it is defined to the last statement of the code.

    If you are using procedures, variable scope gets more complex. Here are some basic rules:

    1. Global - If a variable is defined in the main code, its scope is from the statement where it is defined to the last statement of the entire code including all procedures.

    2. Local - If a variable is defined in a procedure code, its scope is from the statement where it is defined to the last statement of the procedure.

    3. Collision - If a variable is explicitly defined in a procedure code has the same name as a variable defined in the main code, the variable of the main code become in-accessible within this procedure.

    There are some interesting consequences of those rules:

    • The nice thing about rule #1 is that variables defined the main code are automatically accessible in all procedures. You don't have to pass them as reference arguments to share them in a procedure.
    • The bad thing about rule #2 is that if you are using temporary variable in a procedure without explicit declaration, you could accidentally change the value of a global variable of the same name.
    • Rule #3 helps us to avoid the bad impact of rule #3, if you declare all temporary variables explicitly in procedures.

    Example - Variable Scopes

    To help you understand variable scope concepts, I wrote the following sample code, function_variable_scope.html:

    <html>
    <body>
    <!-- function_variable_scope.html
       Copyright (c) 2006 by Dr. Herong Yang. http://www.herongyang.com/
    -->
    <pre>
    <script language="vbscript">
       Dim vGlobalDim
       vGlobalDim = "Cat"
       vGlobalNoDim = "Dog"
    
       Dim vTempDim
       vTempDim = "Bird"
       vTempNoDim = "Fish"
    
       Call ScopeCheck()
    
       document.writeln("")
       document.writeln("Current value after Sub:")
       document.writeln("   vGlobalDim = " & vGlobalDim)
       document.writeln("   vGlobalNoDim = " & vGlobalNoDim)
       document.writeln("   vLocalDim = " & vLocalDim)
       document.writeln("   vLocalNoDim = " & vLocalNoDim)
       document.writeln("   vTempDim = " & vTempDim)
       document.writeln("   vTempNoDim = " & vTempNoDim)
    
    Sub ScopeCheck()
       Dim vLocalDim
       vLocalDim = "Apple"
       vLocalNoDim = "Orange"
    
       Dim vTempDim
       vTempDim = "Banana"
       vTempNoDim = "Grape"
       
    '  Updating values
       vGlobalDim = vGlobalDim & " - Updated by Sub"
       vGlobalNoDim = vGlobalNoDim & " - Updated by Sub"
       vLocalDim = vLocalDim & " - Updated by Sub"
       vLocalNoDim = vLocalNoDim & " - Updated by Sub"
       vTempDim = vTempDim & " - Updated by Sub"
       vTempNoDim = vTempNoDim & " - Updated by Sub"
    
    '  Showing values
       document.writeln("")
       document.writeln("Current value in Sub:")
       document.writeln("   vGlobalDim = " & vGlobalDim)
       document.writeln("   vGlobalNoDim = " & vGlobalNoDim)
       document.writeln("   vLocalDim = " & vLocalDim)
       document.writeln("   vLocalNoDim = " & vLocalNoDim)
       document.writeln("   vTempDim = " & vTempDim)
       document.writeln("   vTempNoDim = " & vTempNoDim)
    End Sub
    </script>
    </pre>
    </body>
    </html>
    

    Here is the output:

    Current value in Sub:
       vGlobalDim = Cat - Updated by Sub
       vGlobalNoDim = Dog - Updated by Sub
       vLocalDim = Apple - Updated by Sub
       vLocalNoDim = Orange - Updated by Sub
       vTempDim = Banana - Updated by Sub
       vTempNoDim = Grape - Updated by Sub
    
    Current value after Sub:
       vGlobalDim = Cat - Updated by Sub
       vGlobalNoDim = Dog - Updated by Sub
       vLocalDim = 
       vLocalNoDim = 
       vTempDim = Bird
       vTempNoDim = Grape - Updated by Sub
    

    Here are my comments about this example:

    • There are 6 variables in this example: vGlobalDim, vGlobalNoDim, vLocalDim, vLocalNoDim, vTempDim, and vTempNoDim.
    • The behavior of vGlobalDim and vGlobalNoDim is pretty consistent, defined in the main code; and accessible in the procedure. "Dim" or not makes no difference.
    • The behavior of vLocalDim and vLocalNoDim is also consistent, define in the procedure, not accessible in the main code. Notice that vLocalDim and vLocalNoDim are empty in the "after Sub" message.
    • The behavior of vTempDim and vTempNoDim shows that "Dim" statement forces vTempDim to a new local variable. So we have two variables of the same name, one in the main code, and one in the procedure. This is why vTempDim still has the old value after the subroutine call.

    Conclusions

    • Be careful about the syntax of calling subroutine without the keyword "Call". You can not put argument list inside parentheses.
    • Function procedure provides the return value through the function name.
    • "Exit Function/Sub" can be used to terminate a procedure.
    • "ByRef" keyword is optional. It shares the data with the calling code.
    • "ByVal" keyword is required. It makes a copy of the data received from the calling code.
    • Variable defined in the main code is globally accessible in any procedure.
    • Variable defined in a procedure is only accessible in that procedure.
    • Recursive calling of a procedure is allowed.
    • I don't know how to specify an array as the return value of a function procedure.

    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      .NET (8)
      Buttons (3)
      Database Related (7)
      Date and Time (1)
      Development (3)
      Error Handling (2)
      File Manipulation (5)
      Introduction to Visual Basic (9)
      Miscellaneous (2)
      Multimedia (9)
      Networking (9)
      Security (1)
      VB Script (6)

    New

    Hot