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.
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:
a lot of copies (array assingment copies in VBScript)
no tail recursion (I doubt, however, that VBScript exploits it)
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
Related
I try to trim spaces in Calc using a macro.
The Trim function doesn't seem to work
I tried using Trim, LTrim, RTrim but same result.
Anybody have an idea of where is wrong?
oCSL is "Arang and the Magistrate"
oCSR and z should also be like oCSL after Trim but still stays "Arang and the Magistrate "
Code below:
Dim oAC As Object, oC As Object, oAS As Object, oCSL As String, oCSR As String
Dim oASn As Integer
Dim CellRangeAddress As New com.sun.star.table.CellRangeAddress
Dim CellAddress As New com.sun.star.table.CellAddress
oC = ThisComponent
oAS = oC.getcurrentcontroller.ActiveSheet
oASn = oC.getCurrentController().getActiveSheet().getRangeAddress().sheet
Dim Ll As Integer, Lc As Integer, r As Integer
Dim z As String, y As String
Ll = 400
Lc = 0
r = 400
' Read Each Left Side Line
Do
Do
' Locate Position For Right Side
Do
oCSL = Trim(UCase(oAS.getCellByPosition(Lc, Ll).String)) ' oSCL Value Returned "Arang and the Magistrate"
oCSR = Trim(UCase(oAS.getCellByPosition(7, r).String)) ' oCSR Value Returned "Arang and the Magistrate "
z = RTrim(oAS.getCellByPosition(7, r).String) ' z Returns Also "Arang and the Magistrate "
If strcomp(oCSL, oCSR) < 0 Then
ThisComponent.CurrentController.Select(oAS.getCellByPosition(7, r)) ' Reposition Selected Area
'ThisComponent.CurrentController.Select(oAS.getCellByPosition(Lc, Ll)) ' Reposition Selected Area
MsgBox ("At " & r & " Not Found " & Chr(13) & oCSL) ' Not Found
' Insert New Movie
Exit Do
else
If UCase(oCSL) = UCase(OcSR) Then
ThisComponent.CurrentController.Select(oAS.getCellByPosition(7, r)) ' Reposition Selected Area
'MsgBox ("At " & r & " Same Value " & oCSL) ' Same Value
r = r + 1
Exit Do
else
r = r + 1
End If
End If
Loop While Lc <= 4
Lc = Lc + 1
Loop While Lc <= 4
Ll = Ll + 1 ' Next Line
Lc = 0 ' Reset Column
Loop While oCSL <> "" ' getCellByPosition Starts From Col 0 Row 0
I found the reason.
After using Asc() in order to figuring out if it's spaces and it wasn't spaces but special characters.
Sorry for bothering everybody.
This code basically takes a mathematical expression and evaluates it.
The following code i have written in VB.net shamelessly taken from here : Expression Evaluation
which has been written in Java.
Public Function evaluate(expression As [String]) As Integer
Dim tokens As Char() = expression.ToCharArray()
' Stack for numbers: 'values'
Dim values As New Stack(Of UInteger)()
' Stack for Operators: 'ops'
Dim ops As New Stack(Of Char)()
For i As Integer = 0 To tokens.Length - 1
' Current token is a whitespace, skip it
If tokens(i) = " "c Then
Continue For
End If
' Current token is a number, push it to stack for numbers
If tokens(i) >= "0"c AndAlso tokens(i) <= "9"c Then
Dim sbuf As New StringBuilder(100)
'Dim sbuf As New String("", 128)
' There may be more than one digits in number
If i < tokens.Length AndAlso tokens(i) >= "0"c AndAlso tokens(i) <= "9"c Then
sbuf.Append(tokens(System.Math.Max(System.Threading.Interlocked.Increment(i), i - 1)))
End If
If sbuf Is Nothing AndAlso sbuf.ToString().Equals("") Then
Else
Dim intgr As Integer
Dim accpt As Boolean = Integer.TryParse(sbuf.ToString(), intgr)
If accpt = True Then
values.Push([Integer].Parse(sbuf.ToString()))
Else
Dim space As String = " "
values.Push(space)
End If
End If
' Current token is an opening brace, push it to 'ops'
ElseIf tokens(i) = "("c Then
ops.Push(tokens(i))
' Closing brace encountered, solve entire brace
ElseIf tokens(i) = ")"c Then
While ops.Peek() <> "("c
values.Push(applyOp(ops.Pop(), values.Pop(), values.Pop()))
End While
ops.Pop()
' Current token is an operator.
ElseIf tokens(i) = "+"c OrElse tokens(i) = "-"c OrElse tokens(i) = "*"c OrElse tokens(i) = "/"c Then
' While top of 'ops' has same or greater precedence to current
' token, which is an operator. Apply operator on top of 'ops'
' to top two elements in values stack
While Not ops.Count = 0 AndAlso hasPrecedence(tokens(i), ops.Peek())
values.Push(applyOp(ops.Pop(), values.Pop(), values.Pop()))
End While
' Push current token to 'ops'.
ops.Push(tokens(i))
End If
Next
' Entire expression has been parsed at this point, apply remaining
' ops to remaining values
While Not ops.Count = 0
values.Push(applyOp(ops.Pop(), values.Pop(), values.Pop()))
End While
' Top of 'values' contains result, return it
Return values.Pop()
End Function
Public Function hasPrecedence(op1 As Char, op2 As Char) As [Boolean]
If op2 = "("c OrElse op2 = ")"c Then
Return False
End If
If (op1 = "*"c OrElse op1 = "/"c) AndAlso (op2 = "+"c OrElse op2 = "-"c) Then
Return False
Else
Return True
End If
End Function
' A utility method to apply an operator 'op' on operands 'a'
' and 'b'. Return the result.
Public Function applyOp(op As Char, b As Integer, a As Integer) As Integer
Select Case op
Case "+"c
Return a + b
Case "-"c
Return a - b
Case "*"c
Return a * b
Case "/"c
If b = 0 Then
'Throw New UnsupportedOperationException("Cannot divide by zero")
End If
Return a \ b
End Select
Return 0
End Function
this is how im using the code :
formula = "10 + 2 * 6"
Dim result As Double = evaluate(formula)
and i keep getting this following error:
Unhandled exception at line 885, column 13 in http:**** DEDOM5KzzVKtsL1tWZwgsquruscgqkpS5bZnMu2kotJDD8R38OukKT4TyG0z97U1A8ZC8o0wLOdVNYqHqQLlZ9egcY6AKpKRjQWMa4aBQG1Hz8t_HRmdQ39BUIKoCWPik5bv4Ej6LauiiQptjuzBMLowwYrLGpq6dAhVvZcB-4b-mV24vCqXJ3jbeKi0&t=6119e399
0x800a139e - Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerServerErrorException: Conversion from string " " to type 'UInteger' is not valid.
Im a beginner but i think that the error is occurring because its not able to covert space into integer.How to deal with the spaces??
Any help is much appreciated:).
VB.NET is strongly-typed, so you simply cannot push anything other than integers onto a Stack(Of Integer). Therefore this code:
Dim space As String = " "
values.Push(space)
will always fail at runtime. (By the way, you want to set Option Explicit On and Option Strict On at the top of every module. If you do that, the line above will already be marked as an error at build time).
I haven't tried executing your code, but why would you need to save the spaces if what you're building is an expression evaluator? It doesn't seem to add anything to the evaluation. Perhaps if you simply don't add the spaces to the stack it will work anyway.
I have a recursive function that creates a list of items based on their hierarchy(integer is used to determine which level 1 to 10 max). There can be x number of items in any given level. I want to store all the items that belong the same level at the corresponding index of the jagged array. The items aren't retrieved based on their level so the level can be jumping around all of over the place as the function recurses.
Function RecurseParts(lngPartID1, lngLevel) As Object
'this function will recursivley print the parts lists for the part ID passed in
If IsNumeric(lngPartID1 & "") Then
Dim objRSTemp As Object = Server.CreateObject("ADODB.Recordset")
objRSTemp.CursorLocation = adUseClient
objRSTemp.Open(PART_LIST_SQL & lngPartID1, objConn, adOpenForwardOnly, adLockReadOnly)
'objRSTemp.ActiveConnection = Nothing
If objRSTemp.eof And objRSTemp.bof Then
'PROBLEM, WE HAVE NO RECORDS
Response.Write("There Were No Parts For This Assembly (Part ID #:" & lngPartID1 & ")")
Else
'make output
Dim strTemp As String = String.Empty
If lngLevel <> 1 Then strTemp = " style=""display: none;"">"
Response.Write("<table id='tblparts" & lngCurrentLineNum & "' border=""0"" cellspacing=""0"" width=""100%"" cellpadding=""1"" " & strTemp)
Do Until objRSTemp.EOF
'increase the current line num
lngCurrentLineNum = lngCurrentLineNum + 1
'get current Part ID
lngCurrentPartID = objRSTemp("PartID").value
'reset flag
blnIsAssm = False
'loop thru array of assemblies to see if this is a parent
For ctr = 0 To UBound(arrAssmList, 2)
If arrAssmList(0, ctr) = lngCurrentPartID Then
'the current part is an assembly
blnIsAssm = True
Exit For
ElseIf arrAssmList(0, ctr) > lngCurrentPartID Then
Exit For
End If
Next
If blnIsAssm Then
'recurse these parts
If RecurseParts(objRSTemp("PartID").value, lngLevel + 1) = True Then
'awesome
End If
End If
objRSTemp.MoveNext()
Loop
Response.Write("</table>")
End If
If objRSTemp.State Then objRSTemp.Close()
objRSTemp = Nothing
'RETURN FUNCTION
RecurseParts = True
Else
'no PART ID passed in
Response.Write("No Part ID Passed In")
RecurseParts = False
End If
End Function
It sounds like a Dictionary would work here.
Dim myDict As New Dictionary(Of Integer, List(Of String))
In your recursive function. The parts in the {} are the parts you have to supply.
'this builds the keys as you go
If Not myDict.ContainsKey({{key} -> your Integer}) Then
'add key and use the From statement to add a value if know at this time
myDict.Add({key}, New List(Of String) From {value})
Else
myDict({key}).Add({string to insert at this level})
End If
List of keys in reverse order:
Dim keys = myDict.Keys.OrderByDescending(Function(k) k)
I was able to create a List to store all the partIDs and their levels.
Dim arrSubID As New List(Of List(Of Integer))()
Function RecurseParts(paramenters)
For ctr = 0 To UBound(arrAssmList, 2)
If arrAssmList(0, ctr) = lngCurrentPartID Then
'checks whether we need a new index in the list
If lngLevel + 1 > arrSubID.Count Then
arrSubID.Add(New List(Of Integer))
End If
'adds the partID where it belongs!
arrSubID(lngLevel).Add(lngCurrentPartID)
blnIsAssm = True
Exit For
ElseIf arrAssmList(0, ctr) > lngCurrentPartID Then
Exit For
End If
Next
End Function
I am making a simple questionnaire for a client in Classic ASP.
The idea is that there will be 10 questions. The user registers and is being sent to the first question. When this is answered they move on to the 2nd question etc.
Questions can be skipped and returned to at a later date, and each question can only be answered once.
I have a comma separated list in the database of each question a user has answered.
So, a user logs in and an array is created with the list of answered questions.
What would be the best way to loop through this list and go to the first unanswered question?
An example of the array of answered questions would look something like this "1,4,6"
so this user would have answered questions number 1, 4 and 6. When a user logs in I'd like to direct them to the first unanswered question, in this case 2. Once the second question is answered the user would be redirected to the next unanswered question.
Any suggestions please?
#Dog, I think this offers the functionality you are looking for.
Tip: See this answer for information on downloading Microsoft's authoritative WSH reference as a Windows help file.
Option Explicit
Dim oQsm : Set oQsm = New QuestionStatusManager
With oQsm
.NumberOfQuestions = 10
.RestoreStatus("1,4,6")
.MarkQuestionAnswered(2)
WScript.Echo "Questions " & .ToString() & " have been answered."
WScript.Echo "Next unanswered question is: " & .GetNextUnansweredQuestion()
End With
Set oQsm = Nothing
' ------------------------------------------------------------------------
Class QuestionStatusManager
Private m_nNumberOfQuestions
Private m_aQuestionList()
Sub Class_Initialize()
m_nNumberOfQuestions = -1
End Sub
Sub Class_Terminate()
Erase m_aQuestionList
End Sub
Public Property Let NumberOfQuestions(n)
Dim bValid : bValid = False
If IsNumeric(n) Then
If n = CInt(n) Then
bValid = True
End If
End If
If Not bValid Then
Err.Raise vbObjectError + 1, "", _
"Value '" & n & "' is not an integer."
End If
m_nNumberOfQuestions = CInt(n)
ReDim m_aQuestionList(n)
End Property
Public Property Get NumberOfQuestions()
CheckState
NumberOfQuestions = m_nNumberOfQuestions
End Property
Private Sub CheckState()
If m_nNumberOfQuestions = -1 Then
Err.Raise vbObjectError + 1, "", _
"Property 'NumberOfQuestions' has not been set."
End If
End Sub
Sub RestoreStatus(sAlreadyAnswered)
CheckState
Dim aAlreadyAnswered : aAlreadyAnswered = Split(sAlreadyAnswered, ",")
Dim i
For i = 0 To UBound(m_aQuestionList)
m_aQuestionList(i) = False
Next
For i = 0 To UBound(aAlreadyAnswered)
m_aQuestionList(CInt(aAlreadyAnswered(i))) = True
Next
End Sub
Sub MarkQuestionAnswered(n)
Dim sDesc
CheckState
On Error Resume Next
m_aQuestionList(n) = True
If Err Or n = 0 Then
sDesc = Err.Description
On Error GoTo 0
Err.Raise vbObjectError + 1, "", _
"Can't mark question number '" & n & "' as answered: " & sDesc
End If
End Sub
Function GetNextUnansweredQuestion()
CheckState
Dim i
For i = 1 To UBound(m_aQuestionList)
If Not m_aQuestionList(i) Then
GetNextUnansweredQuestion = i
Exit Function
End If
Next
GetNextUnansweredQuestion = -1
End Function
Function ToString()
CheckState
Dim sDelim : sDelim = ""
Dim i
ToString = ""
For i = 1 To UBound(m_aQuestionList)
If m_aQuestionList(i) Then
ToString = ToString & sDelim & CStr(i)
sDelim = ","
End If
Next
End Function
End Class
I've tried all the ways I see to add a month to a certain date then return that in a specific format but I'm at a loss. Here's my code but I need to format it:
replace( formatdatetime( dateadd( "m" , 1 , request("date") ), 0 ) , "/" , "-" ) & "' )
request("date") is in yyyyy-dd-mm hh:mm:ss format and that's how I need the new date.
The following should work perfect:
replace( formatdatetime( dateadd( "m" , 1 , cDate(request("date")) ), 0 ) , "/" , "-" )
Notice the use of the cDate function to convert a value to a date explicitly.
Edit:
I removed last part of your code & "' ), it gave me an error otherwise.
When working with dates, it's especially important to take care of the proper data (sub)types. Feeding a string to a function that expects a date (and relying on 'VBScript - and your local settings - will do the right thing') is dangerous.
Using replace will never change the order of the date parts.
FormatDateTime depends on the local/regional settings and should be avoided as a sure path to disaster.
One way to solve this problem + most of all other problems concerning fancy formatting in VBScript is to use a .Net System.Text.StringBuilder:
Given Lib.vbs:
' Lib.vbs - simple VBScript library/module
' use
' ExecuteGlobal goFS.OpenTextFile(<PathTo\Lib.vbs>).ReadAll()
' to 'include' Lib.vbs in you main script
Class ToBeAShamedOf
Public a
Public b
End Class ' ToBeAShamedOf
Class cFormat
Private m_oSB
Private Sub Class_Initialize()
Set m_oSB = CreateObject("System.Text.StringBuilder")
End Sub ' Class_Initialize
Public Function formatOne(sFmt, vElm)
m_oSB.AppendFormat sFmt, vElm
formatOne = m_oSB.ToString()
m_oSB.Length = 0
End Function ' formatOne
Public Function formatArray(sFmt, aElms)
m_oSB.AppendFormat_4 sFmt, (aElms)
formatArray = m_oSB.ToString()
m_oSB.Length = 0
End Function ' formatArray
End Class ' cFormat
and main.vbs:
' main.vbs - demo use of library/module Lib.vbs
' Globals
Dim gsLibDir : gsLibDir = ".\"
Dim goFS : Set goFS = CreateObject("Scripting.FileSystemObject")
' LibraryInclude
ExecuteGlobal goFS.OpenTextFile(goFS.BuildPath(gsLibDir, "Lib.vbs")).ReadAll()
WScript.Quit demoDateFormat()
WScript.Quit main()
Function main()
Dim o : Set o = New ToBeAShamedOf
o.a = 4711
o.b = "whatever"
WScript.Echo o.a, o.b
main = 1 ' can't call this a success
End Function ' main
Function demoDateFormat()
Dim sD : sD = "2012-05-16 01:02:03" ' near future; not yyyyy!
Dim dtD : dtD = CDate(sD)
Dim dtDM : dtDM = DateAdd("m", 1, dtD)
Dim oFmt : Set oFmt = New cFormat
WScript.Echo oFmt.formatArray( _
" sD: {1}{0} dtD: {2}{0} dtDM: {3}{0}dtDM': {4}" _
, Array(vbCrLf, sD, dtD, dtDM, oFmt.formatOne("{0:yyyy-MM-dd hh:mm:ss}", dtDM)))
demoDateFormat = 0 ' seems to be decent
End Function ' demoDateFormat
you'll get:
cscript main.vbs
sD: 2012-05-16 01:02:03
dtD: 16.05.2012 01:02:03
dtDM: 16.06.2012 01:02:03
dtDM': 2012-06-16 01:02:03
(to be seen in the context of this answer)
This may help:
FormatDateTime(DateAdd("M",1,DateSerial(Left(request("date"),4),Mid(request("date"),9,2),Mid(request("date"),6,2))) & " " & Mid(request("date"),12,8),d,0)
It basically converts the string to a valid date in the native format, adds the 1 requested month and then rebuilds the string.
NOTE: request("date") looks as though it returns the current datetime so running it in this way may generate a final value that is a second or so out, if that's a problem then you will be better storing a static value in a variable, otherwise this should hopefully be ok.