For...Next

From Xojo Documentation

Language Keyword

Executes a series of statements a specified number of times. The For Each…Next statement is a variation that executes once for each element of a one-dimensional array.

Usage

For counter [As datatype] = start To | DownTo end [Step value]

[ Statements ]

[ Continue [For] ]
[ Exit [For] ]

[ Statements ]

Next [counter]

Part Description
counter A variable that is incremented or decremented every time the loop executes. It can be an Integer, Single or Double. By default the value is changed by 1 at the end of every iteration, i.e. when the Next statement is reached or a Continue statement is invoked inside the loop. Note that the counter can be changed inside the loop. See the Troubleshooting section.
datatype The Datatype of the counter.

Can be Integer, Single or Double. If you use this optional clause, it declares the counter variable for use inside this loop only.

start Initial value of counter.
To / DownTo Use To to increment the counter at every loop iteration by the Step value (default: 1). Use DownTo to subtract the Step value from the counter at every iteration. You can also count down by using a negative Step value when using To as described in the "Step value" section below.
end Final value of counter. The loop ends if the counter value has reached a value past the end value at the time the Next (or Continue) statement is reached (this means: If the counter gets incremented, the loop ends once counter > end; if the counter gets decremented, the loop ends once counter < end).


Beware: This value gets re-evaluated in every loop iteration, so avoid invoking functions for this value that would incur time intensive operations. Instead, put the end value into a variable before the loop and use that variable as the For statement's end value, as shown in the performance example below.

Step value Amount to increment or decrement counter when a Continue or the loop's Next statement is reached. If Step is not used, the value defaults to 1.

Using To indicates increment; DownTo indicates decrement. If you use a negative value with To then this causes the counter to decrement, providing the same functionality as the DownTo keyword. Only a positive Step value is supported when using DownTo.

Statements Statements to be executed repeatedly inside the loop.
Continue If a Continue statement is present, execution skips directly to the loop's Next statement, thereby causing another loop iteration unless the end is reached.
Exit If an Exit statement is present, execution skips over the remaining statements in the loop and resumes with the statement following the Next statement. See the Exit statement for additional options that are relevant for nested loops.

Notes

Declaring the counter inside the For statement

The counter variable in a For statement can be declared inside the For statement rather than externally, as a local variable.

For example, the code

Var i As Integer
For i = 1 To 10
...
Next
// i is now 11

can also be rewritten as

For i As Integer = 1 To 10
... // i can be used in here
Next
// i is not available at this point any more

In the second code example the counter variable goes out of scope after the last iteration of the For loop, i.e. you can not read the value of the counter after the loop. In the first code example, the counter variable remains accessible after the loop.

Variables declared inside the loop remain private to it

Variables declared inside a loop are not accessible outside of it (which is the same as for other block statements such as If, While and so on).
For example:

For i As Integer = 1 To 5
Var multBy2 As Boolean
multBy2 = i * 2
Next
MessageBox(multBy2.ToString // "multBy2" is out of scope, i.e. you cannot access "multBy2" here. To solve this, move the Var statement before the For statement.

Counting backward

To count from 10 down to 0, you can write (the "Step 1" part is optional):

For loopIndex As Integer = 10 DownTo 0 Step 1

Or:

For loopIndex As Integer = 10 To 0 Step -1

Step usage

Aside from using -1 as a Step value to count backward, you can also use it to change counter by a different value in every loop iteration:

For i As Integer = 1 To 6 Step 2
MessageBox("i = " + i.ToString)
Next

The numbers 1, 3 and 5 will be shown.

Exit and Continue

Use Exit to leave a loop early:

Const attempts = 10
Var attempt As Integer
For attempt = 1 To attempts
Var randomValue As Double = Rnd()
If randomValue > 0.9 Then
MessageBox("Found a random value above 0.9 after " + Str(attempt) + " iterations.")
Exit
End If
Next
If attempt > attempts Then
MessageBox("Found NO random value above 0.9 after " + Str(attempts) + " iterations.")
End If

Similar code using a Continue statement:

Const attempts = 100
Var matchCount As Integer
For attempt As Integer = 1 To attempts
Var randomValue As Double = Rnd()
If randomValue <= 0.9 Then
Continue
End If
matchCount = matchCount + 1
Next
MessageBox("Found " + Str(matchCount) + " random values above 0.9 within " + Str(attempts) + " iterations.")

In the case of nested loops, you can Exit or Continue to the outer loops, provided they are of a different kind (For vs. While vs. Do):

For tries As Integer = 1 To 3 // let's try this up to 3 times
Var misses As Integer
Do
Var randomValue As Integer = Rnd() * 100
If randomValue = 0 And tries > 1 Then
Exit For // Leaves both the Do and the For loops by jumping to #4
End If
If randomValue < 5 Then
Continue For // Leaves the Do loop for another For loop by jumping to #3
End If
If randomValue < 20 Then
Continue Do // Reiterates the Do loop by jumping to #1
End If
misses = misses + 1
If misses > 10 Then
Exit Do // Leaves the Do loop by jumping to #2
End If
Loop // #1
MessageBox("End of Do loop") // #2
Next // #3
MessageBox("End of For loop") // #4

Using Single or Double values as loop counter

The following code shows the values 1.5, 2.5 and 3.5:

For dblValue As Double = 1.5 To 4
MessageBox("Now at " + dblValue.ToString)
Next

Counter value after the loop

If you declare the counter variable outside the loop, it will retain a value after the loop ends. If it finishes normally, i.e., it cycles though every element without encountering Exit, that value will be the end value plus 1 if you have not specified a Step value, or the end value plus Step if you have.

Var i As Integer

For i = 1 To 10
// Some code
Next i
// i = 11

For i = 1 To 10 Step 2
// Some code
Next i
// i = 12

For i = 10 DownTo 1
// Some code
Next i
// i = 0

For i = 10 To 1 Step -2
// Some code
Next i
// i = -1
Var d As Double

For d = 1.0 To 10.0
// Some code
Next d
// d = 11.0

For d = 1.0 To 10.0 Step 0.5
// Some code
Next d
// d = 10.5

If the loop exits early through Exit, the counter will retain the value it held at the point of exit.

Performance considerations

If start, end, or step are not constant values, they get evaluated every time the loop is executed, even if they evaluate to the same value for each iteration. Therefore, the loop:

For i As Integer = 0 To FontCount - 1
...
Next

will take more time than:

Var nFonts As Integer
nFonts = FontCount - 1
For i = 0 To nFonts
...
Next

Be especially aware of this when the end value changes during the loop! Also, you can modify the counter variable inside the loop.

The example below shows both techniques. While it adds new elements to the array, but it will still iterate over all items:

Var values() As Integer = Array(1,5,4,8)
// The loop inserts 0 after values above 4 inside the values array:
For i As Integer = 0 To values.Ubound
If values(i) > 4 Then
values.Insert i+1, 100
i = i + 1 // skip the next item because otherwise we'd add more values endlessly
End If
Next

Miscellaneous

  • For...Next statements can be nested but each For...Next statement must have its own counter variable.
  • When a For loop (same goes for While and Do loops) runs, it blocks the application's user interface, preventing the user from interacting with menus and controls. Ordinarily, this is of no concern. If, however, the loop is very lengthy, you can move the code for the loop to a separate Thread, allowing it to execute in the background and allowing the user interface to remain responsive.
  • When multiple threads are running, be aware that at every loop iteration Xojo might switch to another thread. This means that if you have a time critical loop that should not yield its processing time to other threads, consider adding #pragma BackgroundTasks false in a line before the loop statement.

Troubleshooting

You may have trouble using the For...Next loop, e.g. it exits too soon, it never ends, it processes only one every two values... In such cases, you should set a breakpoint on the For...Next line to verify the counter value.

The most common mistakes are:

  • You have defined two different integers (say, i and j) in a method and you are using both as counters. However, you have copied and pasted some code which also uses i or jj as variables. As a consequence, the computations actually modify the counters you use and mess up the loops.
  • Whenever you plan to delete some items from a list, it is strongly recommended that you write a loop going from the end down to the start to avoid problems with the counter variable.
  • You used a limited Integer type (UInt32 or UInt8) and your loop end reach the minimum or maximum size of the Integer. In order for a loop to exit the counter, it has to go beyond the bounds of the loop's specified end value. For example if your end value is 255, but you used an UInt8 which has a maximum value of 255, then your loop counter can never exceed 255 and you will have an infinite loop. The same logic applies when DownTo is used.

Sample Code

The code below uses the For...Next statement to test the values in an array and change those that are "Today" to "Tomorrow".

Var days() As String = Array("May 1", "Yesterday", "Today", "Next week")
For dayIndex As Integer = 0 To days.Ubound
If days(dayIndex) = "Today" Then
days(dayIndex) = "Tomorrow"
End If
Next

The above code can also be written using the For...Each statement:

Var days() As String = Array("May 1", "Yesterday", "Today", "Next week")
For Each day As String In days
If day = "Today" Then
Var dayIndex As Integer = days.IndexOf(day)
days(dayIndex) = "Tomorrow"
End If
Next

Here is a more advanced example of iterating over the contents of a folder on your disk, collecting the names of folders inside the Desktop folder:

Var folderNames() As String
Var folder As FolderItem = SpecialFolder.Desktop
Var folderCount As Integer = folder.Count // fetch this value before the loop to make the loop run more efficient

For itemIdx As Integer = 1 To folderCount
Var item As FolderItem = folder.ChildAt(itemIdx)
If item = Nil Then Continue // If an item is not accessible, it returns nil
If item.IsFolder Then
folderNames.AddRow(item.Name)
End If
Next

MessageBox("You have the following folders on your Desktop:" + EndOfLine + EndOfLine + Join(folderNames, EndOfLine))

See Also

Continue, Do...Loop, Exit, For...Each, While...Wend statements.