Skipping code using GoTo (Recursion) - vb.net

I have problem skipping some specific part of the code while execution.
Dim turn as Integer = 1
Function recurs1()
If turn = 9 Then
GoTo Endline
End If
For i = 0 To 5
For j = 0 To 5
If (arr(i, j) <> 10 And arr(i, j) <> 20) Then
If chance Mod 2 = 1 Then
MsgBox("Intialized (" & i & "," & j & ") To 10") 'Line X
arr(i, j) = 10
ElseIf chance Mod 2 = 0 Then
MsgBox("Intialized (" & i & "," & j & ") To 20") 'Line Y
arr(i, j) = 20
End If
turn += 1 'Updating turn
recurs1() 'Recursion takes place here
End If
Next
Next
Endline:
Return Nothing
End Function
According to my understanding, the code should stop assigning values to the array after value of turn hits 9 and should return flow to the calling function/sub. However, it continues to assign values and Line X or Line Y are printed depending on the value of turn.
If allowed, the value of turn increments up to 37.
I'd surely appreciate some help with this problem from someone with a deeper understanding of the code than me.
Thank you very much.

You have placed the recursive call inside two for loops. The function will be called recursively until turn = 9, but when it returns it's still in the loop where it will be increased anyway without further checks.
Probably it would work better if you checked the value of turn just before calling the recursive function (this way you get rid of GoTo, also).

Why not just put Return Nothing where you have Goto Endline? Return stops function execution, to the best of my knowledge. That would get rid of the dreaded Goto command (it can cause messy code and inscrutable bugs).

Related

Excel VBA: Jump inside a for loop: "Next without For" – What am I doing wrong?

I honestly do not know why the VBA compiler is nagging me because of that GoTo Jump, respectively Jump:.
counter2 = 0
If (counter1 > 1) Then
For i = 0 To (MaxLastCell - 4)
If (IncompleteRows(i) = 1) Then
If ((counter2 > 1) And (counter2 < counter1)) Then
x = x + ", " + CLng(i)
counter2 = counter2 + 1
GoTo Jump
End If
If ((counter2 > 1) And (counter2 = counter1)) Then
x = x + " and " + CLng(i)
GoTo Outside
If (counter2 = 0) Then
x = CLng(i)
counter2 = 1
End If
End If
Jump:
Next i
Every time I am trying to run my code, this code snippet appears to be a problem. The compiler marks Next at the very bottom and tells me that there is a "Next without For".
But shouldn’t this kind of coding work? I just saw it here. A strange thing, however, is that the compiler there did not seem to coerce B H to move his jumping point NextIteration: to the very left but allowed it to stay at the second indent level and, thus, within the for-loop, as it seems. (Does that even matter?)
try this (revision marked in comments):
counter2 = 0
If (counter1 > 1) Then
For i = 0 To (MaxLastCell - 4)
If (IncompleteRows(i) = 1) Then
If ((counter2 > 1) And (counter2 < counter1)) Then
x = x + ", " + CLng(i)
counter2 = counter2 + 1
GoTo Jump
End If
If ((counter2 > 1) And (counter2 = counter1)) Then
x = x + " and " + CLng(i)
GoTo Outside
ElseIf (counter2 = 0) Then '<--*** changed from simple 'If'
x = CLng(i)
counter2 = 1
End If
End If
Jump:
Next i
End If '<--*** added
but you should avoid GoTos
You've got some nice spaghetti code there. GoTo is but a poor alternative to proper control flow.
One GoTo to "skip to next iteration" is one thing. Another to GoTo Outside (wherever that is) is something else.
VBA (the language spec) doesn't care on which column a line label starts; for all we know the answer you linked to was typed in the answer box, not in the VBE. When the VBE (the IDE/editor) sees a line label, it moves it to column 1 automatically, just like it automatically inserts spaces between operators and operands, and just like it automatically adjusts the casing of keywords and identifiers as you type them. So no, it doesn't matter at all.
VBA syntax requires blocks to be closed: just like a Sub DoSomething() procedure must end with End Sub and a With block must end with End With, a For block must end with Next. Proper indentation and small procedure bodies usually help getting this right.
A lot of other languages (C#, Java, C++, etc.) have similar constraints about what makes a valid code block (mismatched { and } braces are a compiler error in every language that uses them AFAIK), so this isn't VBA being picky or complaining for no reason.
That said it's hard to tell whether and where your code is malformed, because you're not including the entire procedure scope so we have to assume there's nothing else that follows your snippet - and the snippet you've posted is missing an End If as user3598756 has noted:
If (counter1 > 1) Then
'...code...
End If
So, how to go about restructuring this?
Assuming the Outside line label is located just before End Sub (or is it End Function?), then you can replace it with Exit Sub (or Exit Function) and call it a day.
If there's more code that needs to run after the loop but before the end of the procedure scope, Exit For will get you out of the loop while keeping you inside the procedure - next line to run will be the first executable statement that follows the Next token.
Now take the condition that makes the loop skip an iteration, and rephrase the loop body accordingly; use ElseIf to avoid evaluating conditions you don't need to, and remove all these extraneous and confusing parentheses:
If IncompleteRows(i) = 1 And counter2 > 1 And counter2 < counter1 Then
x = x + ", " + CLng(i)
counter2 = counter2 + 1
ElseIf counter2 > 1 And counter2 = counter1 Then
x = x + " and " + CLng(i)
Exit For ' assuming...
ElseIf counter2 = 0 Then
x = CLng(i)
counter2 = 1
End If
And that would be the entire body of the loop. Of course it could still be improved; counter2 > 1 is repeated twice, so there's room for some further restructuring. But already, all GoTo's are gone.

When does VBA change variable type without being asked to?

I am getting a runtime error I don't understand in Excel 2011 for Mac under OS X 10.7.5. Here is a summary of the code:
Dim h, n, k as Integer
Dim report as Workbook
Dim r1 as Worksheet
Dim t, newline as String
Dim line() as String
newline = vbCr
'
' (code to get user input from a text box, to select a worksheet by number)
'
ReDim line(report.Sheets.Count + 10)
MsgBox "Array line has " & UBound(line) & " elements." '----> 21 elements
line = split(t, newline)
h = UBound(line)
MsgBox "Array line has " & h & " elements." '----> 16 elements
n = 0
MsgBox TypeName(n) '----> Integer
For k = h To 1 Step -1
If IsNumeric(line(k)) Then
n = line(k)
Exit For
End If
Next k
If n > 0 Then
MsgBox n '----> 7
MsgBox TypeName(n) '----> String
Set r1 = report.Sheets(n) '----> Runtime error "Subscript out of bounds"
So n is declared as an integer, but now VBA thinks it is a string and looks for a worksheet named "7". Is this a platform bug, or is there something I haven't learned yet?
It also surprises me that putting data into the dynamic array reduces its dimension, but perhaps that is normal, or perhaps for dynamic arrays Ubound returns the last used element instead of the dimension, although I have not seen that documented.
The first part of your question is answered by #ScottCraner in the comments - the correct syntax for declaring multiple strongly typed variables on one line is:
Dim h As Integer, n As Integer, k As Integer
'...
Dim t As String, newline As String
So, I'll address the second part of your question specific to UBound - unless you've declared Option Base 1 at the top of the module, your arrays start at element 0 by default, not element 1. However, the Split function always returns a 0 based array (unless you split a vbNullString, in which case you get a LBound of -1):
Private Sub ArrayBounds()
Dim foo() As String
'Always returns 3, regardless of Option Base:
foo = Split("zero,one,two,three", ",")
MsgBox UBound(foo)
ReDim foo(4)
'Option Base 1 returns 1,4
'Option Base 0 (default) returns 0,3
MsgBox LBound(foo) & "," & UBound(foo)
End Sub
That means this line is extremely misleading...
h = UBound(line)
MsgBox "Array line has " & h & " elements."
...because the Array line actually has h + 1 elements, which means that your loop here...
For k = h To 1 Step -1
If IsNumeric(line(k)) Then
n = line(k)
Exit For
End If
Next k
...is actually skipping element 0. You don't really even need the h variable at all - you can just make your loop parameter this...
For k = UBound(line) To LBound(line) Step -1
If IsNumeric(line(k)) Then
n = line(k)
Exit For
End If
Next k
...and not have to worry what the base of the array is.
BTW, not asked, but storing vbCr as a variable here...
newline = vbCr
...isn't necessary at all, and opens the door for all kinds of other problems if you intend that a "newline" is always vbCr. Just use the pre-defined constant vbCr directly.

Double For Loop in VB.NET

Dim ssi(11) As String
For i = 0 To 10
If ssi(i) = "" Then ssi(i) = "0"
For j = 0 To Val(ssi(11)) + i
ssi(i) = xuh(Val(ssi(i)))
Next
Next
If ssi(11) = "2" Then
L_zz.Caption = Val(Left(ssi(0) & ssi(1) & ssi(2) & ssi(3) & ssi(4) & ssi(5) & ssi(6) & ssi(7), ssi(10)))
ElseIf ssi(11) = "3" Then
L_zz.Caption = Val(Left(ssi(0) & ssi(1) & ssi(2) & ssi(3) & ssi(4) & ssi(5) & ssi(6) & ssi(7), ssi(10))) * (-1)
End If
I am new here and new to VB as well.
I am trying to understand this double loop in vb code.
ssi(i) is defined as a String variable. and each element is assigned to a specific number in a String. Hope I told it clearly.
My problem with this loop is below.
Since i ranges from 0 to 10, what does this j mean? Does j mean the new ssi(1-10) or another whatever number?
I think the best way to answer your question about understanding a double loop is to try looking at something simpler.
The first program I always write in each new version of BASIC that comes along is a 12 times table.
I've modified it a bit below to be a 12 x 10 table for the purpose of illustrating for you how a double loop works ... hope it helps:
For x As Integer = 1 To 12
For y As Integer = 1 To 10
Console.Write(x * y)
Console.Write(vbTab)
Next
Console.WriteLine()
Next

sub declaration visual Basic

I am really new with vba, so please forgive my slaughtering of the code. I'm trying to write a macro for excel (my first one), and I get "statement invalid outside of type block" (pointing at the first line). Here is my code:
Sub MakeHTMLTable()
Worksheets("Sheet1").Activate
endRow As Integer
For Count = 1 To 200
For CountY = 1 To 200
If (!ActiveSheet.Cells(Count, CountY).Value.IsEmpty) Then
ActiveSheet.Cells(Count, CountY).Value = "<td>" + ActiveSheet.Cells(Count, CountY).Value + "</td>"
End If
Exit For
Exit For
For i = 1 To 200
If (!ActiveSheet.Cells(i, 1).Value.IsEmpty()) Then
ActiveSheet.Cells(i, 1).Value = "<tr>" + ActiveSheet.Cells(i, 1)
End If
Exit For
For x = 1 To 200
If (!ActiveSheet.Cells(x, 1).Value.IsEmpty()) Then
endRow = x
End If
Exit For
For countAgain = 1 To 200
If (!ActiveSheet.Cells(x, countAgain).Value.IsEmpty()) Then
ActiveSheet.Cells(x, countAgain).Value = ActiveSheet.Cells(x, countAgain).Value + "</tr>"
End If
Exit For
End Sub
I really don't understand, as the debugger fails on the line of computer generated code without even making it to mine. Have I missed ending an If" or For block?
I also realize that I'm probably reinventing the wheel. Any help on more appropriate built in functions would be appreciated.
It looks like you've got quite a few syntax errors in your code.
In VBA, local variables are declared with the Dim keyword.
So, the endRow declaration should look like this:
Dim endRow As Integer
For loops should end with the Next statement. So, your For loops should look like this:
For x = 1 To 200
If (!ActiveSheet.Cells(x, 1).Value.IsEmpty()) Then
endRow = x
End If
Next
VBA uses the keyword Not instead of !, so, your conditionals should look like this:
If (Not (ActiveSheet.Cells(i, 1).Value.IsEmpty())) ...
Try removing most of the code and adding it back in line-by-line until all of it works. The VBA syntax can be cumbersome for those who aren't used to it.

This array is fixed or temporarily locked

I am using split function and assigning the value in a variable and running the code in loop after few iterations its giving an error of "This array is fixed or temporarily locked (Visual Basic)"..
e.g; here value of movies_cat1 read from excel is in form of this------
"Movies->List All Movies , Movies->World Cinema->Asia , Movies->Movies by Language->Sinhalese , Movies->Drama"
For crow = 1 To 100
Value = Worksheets("Movies_categories").Range("A" & crow).Value
cat_final = Worksheets("Movies_categories").Range("B" & crow).Value
If Value = "y" Or Value = "Y" Then
'Loop for reading the data from tabsheet- Movies
For crowss = 5 To 3000
movies_cat1 = Worksheets("Movies").Range("B" & crowss).Value
movies_language = Worksheets("Movies").Range("C" & crowss).Value
If movies_language = "English" Then
Temp = Split(movies_cat, ",") 'run time Error:10 occurs here..
For Each boken_c In Temp
flag = 0
boken_c = Trim(boken_c)
If RTrim(LTrim(boken_c)) = LTrim(RTrim(cat_final)) Then
flag = 1
GoTo Line4:
End If
Next boken_c
End If
Next crowss
End If
Line4: Next crow
Error occurs at this statement: Temp = Split(movies_cat, ","), it says that the array is fixed or temporarily locked, because i think initially its taking 'temp' as a variable, but while returning the value of split function, variable 'Temp' becomes array after completion of first loop(i.e after crow = 6,7....)
Your line4 label is outside the for loop on the temp variable so when you goto it leaves it locked.
You really should restructure your code to not use a goto inside the for each loop.
Maybe:
For crow = 1 To 100
Value = Worksheets("Movies_categories").Range("A" & crow).Value
cat_final = Worksheets("Movies_categories").Range("B" & crow).Value
If Value = "y" Or Value = "Y" Then
'Loop for reading the data from tabsheet- Movies
For crowss = 5 To 3000
movies_cat1 = Worksheets("Movies").Range("B" & crowss).Value
movies_language = Worksheets("Movies").Range("C" & crowss).Value
If movies_language = "English" Then
Temp = Split(movies_cat, ",") 'run time Error:10 occurs here..
For Each boken_c In Temp
flag = 0
boken_c = Trim(boken_c)
If RTrim(LTrim(boken_c)) = LTrim(RTrim(cat_final)) Then
flag = 1
**Exit For**
End If
**If flag = 1 Then Exit For**
Next boken_c
End If
**If flag = 1 Then Exit For**
Next crowss
End If
Next crow
(Note the **d lines.)
I had this problem too with VBA. I cannot say I am proud of how I managed to get it, but it is supplied here just in can anyone else accidentally slips up on this.
It is quite interesting to debug as the failure occurs at the call to a sub or function - not at the point of failure. Luckily, you can follow the code through to the offending line of the called routine before it reports the error.
Call Sub1(gArray(3))
debug.print gArray(3)
...
Sub Sub1(i as integer)
Redim gArray(0)
End sub
Clearly the VB RT is not going to like this as by the time the debug.print executes, the array dimension does not exist. Ok why the hell would you want to pass a globally declared array anyway? Guilty as charged.
So in the example above you get the error at the call to Sub1, but the thing causing it is the Redim in the sub.
The moral to the story is do not pass global variables as parameters to subs. Another simple fix is to declare the Sub1 slightly differently:
Sub Sub1(ByVal i as integer)
This means the i variable is copied (but not returned) by the Sub.
Thanks, Deanna for the answer! I have a similar message on ReDim Preserve statement at the last line in next fragment of code:
If StrComp(Mid(s, 1, lE), txtE, vbBinaryCompare) = 0 Then
For i = iMdl To 1 Step -1
With blks(i)
If .lnEnd = 0 Then ' ".lnEnd" is a member of blks(i)
.lnEnd = ln
GoTo NXT
End If
End With
Next
errBegWith = errBegWith & ln & ", "
NXT:
End If
'...
ReDim Preserve blks(iMdl)
And after extracting assignment .lnEnd = ln from inside of the With the program works fine:
If StrComp(Mid(s, 1, lE), txtE, vbBinaryCompare) = 0 Then
For i = iMdl To 1 Step -1
If blks(i).lnEnd = 0 Then
blks(i).lnEnd = ln
GoTo NXT
End If
Next
errBegWith = errBegWith & ln & ", "
NXT:
End If
'...
ReDim Preserve blks(iMdl)