0

I wrote vbscript function which search the list of free ports on Unix. So my code looks like this:

Function FindFreePorts(ByVal intPortToStart, ByVal intCountOfPortToSearch, ByVal arrFreePorts)
   Dim i, arrCommand, arrTmp, blnFindAllPort, j, strCommand
   Dim blnFree, intCountOfFreePorts

   strCommand = "lsof -i | awk '{print $8}' | sed -n '/"    
   For i = intPortToStart To intPortToStart+intCountOfPortToSearch - 1
       If i = intPortToStart+intCountOfPortToSearch - 1 Then
           strCommand = strCommand & ".*"& i & "$/s/.*://p'"
       Else
           strCommand = strCommand & ".*:" & i &"\|"
       End If     
   Next

   Push arrCommand, strCommand  
   arrTmp = Array() 
   arrTmp = ExecuteCommandOnUnix(arrCommand, Null, _
                                                                        Environment.Value("UNIXHOST_NAME"), _
                                                                        Environment.Value("UNIXHOST_USER"), _
                                                                        Environment.Value("UNIXHOST_PWD"))  
   ' Count of busy port is UBound(arrTmp) - the other ports are free
   ' Find free ports
   intCountOfFreePorts = intCountOfPortToSearch 
    For i = intPortToStart To intPortToStart+intCountOfPortToSearch - 1
        blnFree = True
        For j = 0 To UBound(arrTmp)
            If arrTmp(j) = CStr(i) Then
                blnFree = False
                j = UBound(arrTmp)
            End If
        Next
        If blnFree Then
            Push arrFreePorts, i
            intCountOfFreePorts = intCountOfFreePorts - 1
        End If
    Next
    '
   If  intCountOfFreePorts = 0 Then
       blnFindAllPort = True
   Else
        blnFindAllPort = False
   End If

   If Not blnFindAllPort Then
       ' We found UBound(arrFreePorts), we need to find intCountOfPortToSearch - UBound(arrFreePorts) 
       Do While intCountOfPortToSearch - UBound(arrFreePorts) - 1 <> 0
                arrFreePorts = FindFreePorts(intPortToStart + intCountOfPortToSearch + 1, intCountOfPortToSearch - UBound(arrFreePorts) - 1, arrFreePorts)
                If intCountOfPortToSearch - UBound(arrFreePorts) - 1 = 0 Then
                    Exit Do
                End If
       Loop
   End If

    FindFreePorts = arrFreePorts
End Function

As you can see I call this function recursively in Do While cycle. Everything works ok but I don't like arrFreePorts parameter here. So I should write this code to execute my function:

arrPort = FindFreePorts(intStartFromPort, intCountToSearch, arrPort)

But I have no idea how to rewrite this function without this parameter. Then I could call it in more simple way:

arrPort = FindFreePorts(intStartFromPort, intCountToSearch)

Thanks in advance.

2
  • 2
    If I'm reading your code correctly, it looks like you're passing a copy of your array with every function call. And since you're using recursion, that could be a lot of copies! Why not just declare an array in your calling scope (the scope that originally calls FindFreePorts) and pass that array, by reference, to FindFreePorts. Now you're just passing a reference to your array instead of a copy of it each time. And you don't have to return it so your Function can become a Sub. You'll still need your Push routine to grow the array dynamically, though. Commented Jul 10, 2014 at 13:21
  • Thanks, did as you told, everything works as expected but more faster :) Commented Jul 11, 2014 at 8:15

1 Answer 1

1

To keep things (experiments, illustration of @Bond's contribution) simple, let's consider the task of putting the chars of a string into an array.

A function that shall return an array (without getting it via parameter or global) needs to create the array:

Function f1(s) ' default ByRef is ok, because Left and Mid return new (sub)strings
  If "" = s Then
     f1 = Array() ' return empty array for empty string
  Else
     Dim t : t = f1(Mid(s, 2)) ' recurse for tail - sorry no tail recursion
     f1 = cons(Left(s, 1), t) ' copy! result of cons to f1/function's return value
  End If
End Function

The growing of the array could be done inline, but for clarity I use a helper function cons():

Function cons(e, a) ' default ByRef is ok; neither e nor a are changed
  ReDim aTmp(UBound(a) + 1) ' create larger array
  Dim i : i = 0
  aTmp(i) = e ' 'new' head
  For i = 1 To UBound(aTmp)
      aTmp(i) = a(i - 1) ' copy 'old' elms
  Next
  cons = aTmp ' copy! aTmp to cons/function's return value
End Function

Calling the function is nice:

  WScript.Echo 3, "[" & Join(f1("12456789"), ",") & "]"

The price to pay for avoiding the passing of 'the' array:

  1. a lot of copies (array assingment copies in VBScript)
  2. no tail recursion (I doubt, however, that VBScript exploits it)
  3. about factor 10 slower than the Sub approach ((c) Bond) below

As in this case the recursive calls can/should work on the 'same' array, a Sub does the task more efficiently (and is less complex):

Sub s1(s, a) ' default ByRef is ok; a should be modified, s isn't touched
  If "" <> s Then
     ReDim Preserve a(UBound(a) + 1) ' grow! a
     a(UBound(a)) = Left(s, 1)
     s1 Mid(s, 2), a ' tail recursion for tail
  End If
End Sub

Calling it looks nasty:

  ReDim a(-1) : s1 "123456789", a : WScript.Echo 3, "[" & Join(a, ",") & "]"

But that can be avoided by a wrapper:

Function f2(s)
  ReDim aTmp(-1)
  s1 s, aTmp
  f2 = aTmp
End Function
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.