I've just transferred into a computer class at my high school, we're learning VB 2010. I've been studying hard, but this question has me stumped:
"Two procedures with parameters are shown below. One uses a pre-tested loop and the other uses a post-tested loop. After making the procedure call,
pattern (-2,"####")
The output from each procedure is different.
PRE TESTED
pattern (start as integer, hash as string)
DIM counter as integer
Counter = 0
DO WHILE counter < start
lbldisplay.text = hash
Counter = counter + 1
LOOP
END SUB
POST TESTED
pattern(start as integer, hash as string)
DIM counter as integer
Counter=0
DO
lbldisplay.text = hash
Counter = counter + 1
LOOP WHILE counter > start
END SUB
What would happen in each procedure? What would the result be? I've got my head around some of the basics but this has really stumped me. Thanks!
In Short: a "POST-TESTED" loop will always execute at least once. Where as a "PRE-TESTED" loop may not execute at all.
Related
As a noobie to VBA, I am having a Hell of a time understanding how arrays, dynamic arrays in specific, work. I am an Industrial Engineering student, so most of my coding has been done with MatLab.
My question is: Why do I keep getting Run-time error '9' "Subscript out of range" for the following code:
Sub FunWithArrays()
Dim DynArray()
Dim i As Integer
Dim j As Integer
j = 1
For i = 1 To 10
DynArray(j) = i
j = j + 1
ReDim DynArray(UBound(DynArray) + 1)
MsgBox DynArray(i)
Next
End Sub
Any and all help is appreciated!
As people in the comments have mentioned, dynamic arrays are just that: dynamic. That is to say that if you declare an array without dimension, as you have here with Dim DynArray() then it doesn't at this point have any "slots" to store anything.
When an array is declared this way, you then need to Redim it to tell it how many slots you want it to have (presumably after determining this from some other data or user input).
The full code for this would be:
Sub FunWithArrays()
Dim DynArray()
Dim i As Integer
Dim j As Integer
j = 1
Redim DynArray(10)
' redimensioned the array to hold 10 items (since
' you've set the loop as i=1 to 10 you must already
' know that the array wants to hold 10 things)
For i = 1 To 10
'ReDim Preserve DynArray(i)
' this is how you could do it if you didn't know in
' advance that you were going to loop 10 times,
' but as somebody else said, this is a slow way to do it.
DynArray(i) = j
j = j + 1
MsgBox DynArray(i)
' Generally instead of a messagebox here I would use:
' Debug.Print DynArray(i)
' this then prints out the value (on a new line each time)
' into the immediate window which means you can see the
' output without having to click "OK" each time.
Next
End Sub
You can also redimension the array within the loop, or later on, to hold a different number of items, but if you want to keep the items already stored in the array you must use ReDim:
ReDim Preserve DynArray(i)
It's also good practice in general to declare the array type. When you use Dim DynArray() this creates an array of type Variant which can hold anything (String, Double, Object, etc.). If you explicitly declare the type e.g. Dim DynArray() as Integer then it'll only allocate enough memory to hold integers, which is more efficient. For many applications the difference in speed will not make a difference, but when you're dealing with looping through something thousands of times it can matter.
Alright, so I am editing a Macro for Reflection Workspace Terminal Emulator. I have a macro (it is long and not necessarily relevant, I can post the full code if anyone wants, it is about a page)
At the very end of the macro, a "Good Morning" Message is printed, and then also the value of a string variable named myName. This works fine.
What I want to do is then use a For loop to print a number of control characters (tab) equal to the amount printed in the Good Morning User. Here is the code I have so far:
Dim X As Integer
For i = 1 To i = (13 + Len(myName)) 'Good Morning + a space character will always = 13
ibmCurrentScreen.SendKeys (ControlKeyCode_Tab)
Next i
End Sub
I would use the Chr() function to print the control characters, but it seems that this particular program uses ControlKeyCode_X to print them.
The For...Next loop in VBA is a simple single variable counter based loop and cannot have complex criteria or multiple counters like in some other languages (e.g. in C# for(int i = 0, int j = 10; i < 10; i++, j--)). In VBA, therefore, the i = after To is redundant because it cannot be anything other than i.
The 'For' loop in VBA therefore only specifies the counter once. It should be:
For i = startVal To endVal [step incrementVal]
...
Next [i]
i = variable to vary
startVal = start value
endVal = end value (inclusive, i.e. To 10 will include 10 as the final value)
incrementVal = optional value to increment/decrement by each loop
(default = 1)
The counter is always incremented/decremented at the end of each loop then evaluated against endVal (i.e. for a vanilla increment by one loop, after exiting the loop it will be endValue + 1).
Note (unrelated to your situation) that you can change the value of i in the loop, e.g. to increment twice because of some special circumstance.
Specifying the variable with the Next statement is optional and has no effect on behaviour (any nested loop must always be closed before an outer loop) but is good practice for reference when you have many For...Next loops so it is clear which loop the Next is closing (although you should be religiously indenting or otherwise delineating your code)
So in your case For i = 1 To (13 + Len(myName)) instead.
I am trying to run a for loop for a backup system and inside that i want to run a SP that will loop. Below is the code that does not work for me..
Any ideas please?
Dim TotalTables As Integer
Dim i As Integer
TotalTables = 10
For i = 1 To TotalTables
objDL.BackupTables(220, i, 001) ' (This is a method from the DL and the 3 parameters are integars)
Next
I tried the SP and it works perfectly in SQLServer
Third parameter is bad, should be:
objDL.BackupTables(220, i, 1)
I want to iterate over all rows of a MS-Word mail merge data source and extract the relevant data into an XML.
I'm currently using this code:
Imports Microsoft.Office.Interop
Do
objXW.WriteStartElement("Recipient")
Dim objDataFields As Word.MailMergeDataFields = DataSource.DataFields
For Each FieldIndex As Integer In mdictMergeFields.Keys
strValue = objDataFields.Item(FieldIndex).Value
If Not String.IsNullOrEmpty(strValue) Then
strName = mdictMergeFields(FieldIndex)
objXW.WriteElementString(strName, strValue)
End If
Next
objXW.WriteEndElement()
If DataSource.ActiveRecord = LastRecord Then
Exit Do
Else
DataSource.ActiveRecord = Word.WdMailMergeActiveRecord.wdNextDataSourceRecord
End If
Loop
And it turns out to be a little sluggish (About 1 second for each row). Is there any way to do it faster?
My fantasy is finding a function like MailMergeDataSource.ToDatatable and then inspecting the datatable.
Any time you're iterating through something row by row, and then doing some kind of processing on each row, is going to get a little slow.
I would be inclined to approach this problem by having a step before this which prepared the mdictMergeFields collection so that it only contained elements that were not 'null or empty', this will mean you won't have to check for that on each iteration. You could do this in process, or 'sneakily' in the background while the user is doing something else.
The other thing to try (might help!) is to change the "Do... Loop" block so that you're not checking at the end of each imported row whether or the record is the 'last record'. Instead, get a count of the records, and then compare the current index to the knowm maximum (which might be quicker)
I.E.:
Dim i, x as Integer
i = ActiveDocument.MailMerge.DataSource.RecordCount
Do While x < i
objXW.WriteStartElement("Recipient")
Dim objDataFields As Word.MailMergeDataFields = DataSource.DataFields
For Each FieldIndex As Integer In mdictMergeFields.Keys
strValue = objDataFields.Item(FieldIndex).Value
If Not String.IsNullOrEmpty(strValue) Then
strName = mdictMergeFields(FieldIndex)
objXW.WriteElementString(strName, strValue)
End If
Next
objXW.WriteEndElement()
x += 1
Loop
I don't really work with the Office Interop much, but hopefully this might offer some assistance! Post back, let me know how it goes.
/Richard.
Is there an easy way in either language to generate a large set of random data quickly so far all the functions I've tried haven't worked too well when I need to generate a group of say 500,000 characters :( Any ideas?
Use UUIDGen.
Don't. GUIDs aren't really random. You can actually generate large amounts of data very fast using the System.Random class in VB.NET. 500,000 characters/bytes are no problem:
Dim buffer As Byte() = Nothing
Array.Resize(buffer, 500000)
Call New Random().NextBytes(buffer)
My.Computer.FileSystem.WriteAllBytes("filename", buffer, False)
This code takes considerably less than one second.
In VB6 the code would go something like this
Public Function FillRandomCol() as Collection
Dim C As Collection
Dim I As Long
Set C = New Collection
Randomize Timer
For I = 1 To 500000
C.Add RandomChar
Next I
Set FillRandomCol = C
End Sub
Public Function Random(ByVal Number As Integer) As Integer
Random = CLng(Rnd * 1000000) Mod Number + 1
End Function
Public Function RandomChar() As String
Const AlphaNum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
RandomChar = Mid$(AlphaNum, Random(36), 1)
End Function
Takes 1/2 second on a 2 Core Intel 2.40 GHz computer.
Use UUIDGen. At least the chunks will be bigger.