无需额外参数即可递归调用函数

时间:2014-07-10 11:28:28

标签: recursion vbscript

我编写了vbscript函数,用于搜索Unix上的空闲端口列表。所以我的代码看起来像这样:

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

如您所见,我在Do While周期中递归调用此函数。一切正常,但我不喜欢这里的arrFreePorts参数。所以我应该编写这段代码来执行我的函数:

arrPort = FindFreePorts(intStartFromPort, intCountToSearch, arrPort)

但我不知道如何在没有此参数的情况下重写此函数。然后我可以用更简单的方式调用它:

arrPort = FindFreePorts(intStartFromPort, intCountToSearch)

提前致谢。

1 个答案:

答案 0 :(得分:1)

为了保持事物(实验,@ Bond的贡献的说明)简单,让我们考虑将字符串的字符放入数组中的任务。

一个返回数组的函数(不通过参数或全局获取)需要创建数组:

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

数组的增长可以内联完成,但为了清楚起见,我使用辅助函数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

调用该函数很好:

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

避免传递'the'数组的代价:

  1. 许多副本(VBScript中的数组分配副本)
  2. no tail recursion(我怀疑VBScript是否利用它)
  3. 比在
  4. 下面的Sub方法((c)Bond)慢10倍

    作为在这种情况下,递归调用可以/应该在'相同'数组上工作,Sub可以更有效地完成任务(并且不那么复杂):

    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
    

    称它看起来很讨厌:

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

    但是这可以通过包装器来避免:

    Function f2(s)
      ReDim aTmp(-1)
      s1 s, aTmp
      f2 = aTmp
    End Function