In a classic ASP function, when I do a loop inside another as shown in the code below I have a stack overflow error.
Function shift(x,y)
shift = x
For i = 1 to y
shift = shift*2
Next
End Function
Function translate_url(iVal)
sAlpha = "abcdefghijklmnopqrstuvwxyz-_0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
doWrite = False
iBase = 63 'DO NOT CHANGE
For i = 4 to 0 step -1
iPos = (iVal and shift(iBase, i*6))/shift(1, i*6)
If iPos Then doWrite = True
If doWrite Then translate_url = translate_url & Mid(sAlpha, iPos + 1,1)
Next
End Function
arr = Split("1,2,3,4,5,6,7,8,9,0",",")
For Each i In arr
response.Write(translate_url(arr(i)))
next
The error does not occur when I remove the loop outside the function. Eg:
response.Write(translate_url(arr(1)))
return "c".
What I need to do to make the code flows down the array and return the corresponding values according to the function?
VBScript has a dark side. Variables scope is one of them.
When you don't declare a variable, VBScript will do it for you, free of charge or error and give it global scope.
What does it mean? Take a look in the main loop:
For Each i In arr
response.Write(translate_url(arr(i)))
next
The i variable becomes global. When you have this later in the function:
For i = 4 to 0 step -1
'...
Next
It's changing the same i variable. This is causing endless loop of function calls.
To resolve this, declare i locally in each function:
Function shift(x,y)
Dim i
'...
End Function
Function translate_url(iVal)
Dim i
'...
End Function
And it will be different variable and no overflow.
As the EVIL global variable i is used in your top level loop and in the functions shift() and translate_url(), you got what you deserve.
Evidence: Just change your loop to
For Each NoliMeTangere In arr
response.Write translate_url(arr(NoliMeTangere))
next
Remedy: Use "Option Explicit" and Dim all local variables in your Subs/Functions/Methods.
Related
If I have a recursive function (Lotusscript) and inside the function is a locally declared variable, will each iteration of the call keep the variables independently stored in memory?
For example, I have a counter on a main function that loops 10 times....it calls the recursive function, and in certain circumstances, that will call itself....but passing a different object each time as an argument. The recursive function has its own counter variable declared locally.
Suppose this main function if called, and it calls the recursive function one time, and begins a loop, counting up to ten itself. On the 5th loop, it calls itself. This recursion will end due to setting a global boolean, and now I have three known local variables, the main function, and two from the recursive function.
Will each of these counters be kept track of independently, so that depending on which function I am in it knows where it is in its own ten loops?
I hope I made this clear. I am trying a simple proof-of-concept function but it is really confusing.
Thanks
Yes it will be independant: the local variables are local to each call within the recursion as long as you don‘t use them as parameters as they are byref by default:
Sub RecurseMe( intParameter as Integer )
Dim intCount as Integer
Print "Called with:", intParameter
intParameter = intParameter + 1
intCount = intCount + 1
Print "IntCount: ", intCount
If intParameter < 3 then
Call RecurseMe( intParameter )
End If
Print "Exiting with: ", intParameter
End Sub
Dim intTest as Integer
intTest = 1
Call RecurseMe( intTest )
Print "Final result: ", intTest
Will output:
Called with: 1
IntCount: 1
Called with: 2
IntCount: 1
Exiting with: 3
Exiting with: 3
Finale result: 3
As your see: intCount is always reinitialised in sub, intParameter will even be changed in the calling sub.
Suppose I have the following code which has two nested while loops.
struct Parameters
maxIter1::Float64
maxIter2::Float64
tolerance1::Float64
tolerance2::Float64
end
mutable struct Guess
x1::Float64
x2::Float64
end
function solveModel(par::Parameters,initGuess::Guess)
iterate1 = 0
error1 = 0
guess = initGuess
while (par.iterate1 < par.maxIter1 && error1 > par.tolerance1)
iterate1 += 1
iterate2 = 0
error2 = 0
guess.x2 = initGuess.x2
while (iterate2 < par.maxIter2 && error2 > par.tolerance2)
iterate2 += 1
z2 = solveInnerProblem(par,guess)
newGuess = update2(par,guess,z2)
error2 = computeError2(newGuess,guess)
guess = newGuess
end
guess = newGuess
end
end
I get an error message,
Note: the reference to the line number is erroenous - line 294 of my code contains no mention whatsoever of newGuess.
The error message goes away if I comment out the line
guess = newGuess
In the outer loop (last line before the final two end lines in the code snippet). I'm quite confused as to why this is happening. The variable newGuess is clearly defined, but Julia says it is not defined...
newGuess is a local variable, which means that it is defined in a localized part of the program rather than all the program. In the case of a local variable defined within a loop like a while statement, the variable is undefined outside the while loop within which it is defined, which is the inner while loop of your function. So the "not defined" error is because the program is trying to access the variable outside of its local scope-- it was defined before, but not when the error occurs.
You may need to define newGuess higher up, within the function, but before the inner while statement.
This is really getting annoying. The following is a method which is contained in a custom database class. I query data into a recordset then try to convert this data into an array w/o field names. It appears to be working within the function because I setup a response.write to check if there were any values. But once out of the function, things go haywire (the array is not the same).
Public Function To2Array()
dim A, x, columns
columns = Rs.Fields.Count
Rs.MoveFirst()
x = 0
do until Rs.EOF
Redim A(x+1,columns)
for y = 0 to columns - 1
A(x,y) = Rs.Fields.Item(y)
response.write A(x,y) 'returns correct value
Next
x = x + 1
Rs.MoveNext()
loop
To2Array = A
End Function
I assign the return array but there appears to be nothing.
arr = db.To2Array()
response.write(arr(1,0)) 'returns nothing
Can anyone figure out what I'm doing wrong?
You can only grow the last dimension of an VBScript array. So you
need an colsXrows array.
To keep the 'old' part of a dynamic array, you must ReDim Preserve.
To get a two dimensional array from a recordset, use .GetRows - and avoid all risks of 'rolling your own'.
You lose the values in A every time you redim it. Using redim preserve prevents this, but you can only redim the last array dimension when you use preserve.
Given the following:
wscript.echo "fx(0)=" & fx(0)
Function fx( v1 )
fx = 1 + 2
Wscript.echo "fx=" & fx
End Function
Both "echo" lines print the value 3. Why doesn't the one inside the function cause a syntax error or recursive loop? Same question with following sample:
wscript.echo "fy()=" & fy()
wscript.echo "fy=" & fy
Function fy
fy = 1 + 2
Wscript.echo "fy=" & fy
End Function
All echo lines print 3.
I can't find documentation that describes the behavior when a function definition references it's name as an RVALUE.
TIA.
Inside a VBScript function, the name of the function is a locally scoped variable intended to be equivalent to the return value that comes out of the function. It's no different from any other Dim var = .... To treat it as a function, you need to use call semantics.
Basically, you seem to have gotten used to languages that have first-class functions. VBScript doesn't have them, so there is no ambiguity.
In addition to Plynx, VBScript is somewhat ambigeous. That is why you can use wscript.echo fy as well as wscript.echo fy() for functions without parameters.
However, this doesn't work the other way around inside functions where you use the functionname as variable. If you do this:
Function fy
fy = 1 + 2
Wscript.echo "fy=" & fy()
End Function
You'll get an Out of stack space: 'fy' error.
Using the functionname as a function can actually be usefull, because you can now use the functionname as a variable or as a function call whilest building recursive functions.
For readability, I'll recommend to use parenthesis in a function call. It will make reviewing or debugging easier for you or your co-worker.
I have a large classic ASP app that I have to maintain, and I repeatedly find myself thwarted by the lack of short-circuit evaluation capability. E.g., VBScript won't let you get away with:
if not isNull(Rs("myField")) and Rs("myField") <> 0 then
...
...because if Rs("myField") is null, you get an error in the second condition, comparing null to 0. So I'll typically end up doing this instead:
dim myField
if isNull(Rs("myField")) then
myField = 0
else
myField = Rs("myField")
end if
if myField <> 0 then
...
Obviously, the verboseness is pretty appalling. Looking around this large code base, the best workaround I've found is to use a function the original programmer wrote, called TernaryOp, which basically grafts in ternary operator-like functionality, but I'm still stuck using a temporary variable that would not be necessary in a more full-featured language. Is there a better way? Some super-secret way that short-circuiting really does exist in VBScript?
Nested IFs (only slightly less verbose):
if not isNull(Rs("myField")) Then
if Rs("myField") <> 0 then
Maybe not the best way, but it certainly works... Also, if you are in vb6 or .net, you can have different methods that cast to proper type too.
if cint( getVal( rs("blah"), "" ) )<> 0 then
'do something
end if
function getVal( v, replacementVal )
if v is nothing then
getVal = replacementVal
else
getVal = v
end if
end function
I always used Select Case statements to short circuit logic in VB. Something like..
Select Case True
Case isNull(Rs("myField"))
myField = 0
Case (Rs("myField") <> 0)
myField = Rs("myField")
Case Else
myField = -1
End Select
My syntax may be off, been a while. If the first case pops, everything else is ignored.
If you write it as two inline IF statements, you can achieve short-circuiting:
if not isNull(Rs("myField")) then if Rs("myField") <> 0 then ...
But your then action must appear on the same line as well. If you need multiple statements after then, you can separate them with : or move your code to a subroutine that you can call. For example:
if not isNull(Rs("myField")) then if Rs("myField") <> 0 then x = 1 : y = 2
Or
if not isNull(Rs("myField")) then if Rs("myField") <> 0 then DoSomething(Rs("myField"))
Or perhaps I got the wrong end of the question. Did you mean something like iIf() in VB? This works for me:
myField = returnIf(isNothing(rs("myField")), 0, rs("myField"))
where returnIf() is a function like so:
function returnIf(uExpression, uTrue, uFalse)
if (uExpression = true) then returnIf = uTrue else returnIf = uFalse : end if
end function
Yeah it's not the best solution but what we use is something like this
function ReplaceNull(s)
if IsNull(s) or s = "" then
ReplaceNull = " "
else
ReplaceNull = s
end if
end function
Would that there were, my friend -- TernaryOp is your only hope.
Two options come to mind:
1) use len() or lenb() to discover if there is any data in the variable:
if not lenb(rs("myField"))=0 then...
2) use a function that returns a boolean:
if not isNothing(rs("myField")) then...
where isNothing() is a function like so:
function isNothing(vInput)
isNothing = false : vInput = trim(vInput)
if vartype(vInput)=0 or isEmpty(vInput) or isNull(vInput) or lenb(vInput)=0 then isNothing = true : end if
end function
You may be able to just use Else to catch nulls, ""s, etc.
If UCase(Rs("myField")) = "THING" then
'Do Things
elseif UCase(Rs("myField")) = "STUFF" then
'Do Other Stuff
else
'Invalid data, such as a NULL, "", etc.
'Throw an error, do nothing, or default action
End If
I've tested this in my code and it's currently working. Might not be right for everyone's situation though.