One feature that was added in Xojo 2014r3 that I haven’t seen much discussion about yet is iterators. In short, iterators are a way to make classes useable with the existing For Each loop feature.
With the new framework, we’ve taken the opportunity to make several built-in classes iterable. For example, each character in a Text value can be accessed like so:
Dim myText As Text = "Hello world" For Each char As Text In myText Xojo.System.DebugLog(char) Next
There are two class interfaces involved with iterators: Xojo.Core.Iterator and Xojo.Core.Iterable. Iterators are responsible for providing the values used by the For Each loop. Iterable objects are responsible for creating the iterators. Here are the interface declarations:
Interface Iterator Function MoveNext() As Boolean Function Value() As Auto End InterfaceInterface Iterable Function GetIterator() As Iterator End Interface
At the beginning of the for loop, the iterable is asked to create an iterator via GetIterator. Next the loop invokes MoveNext on the iterator, which positions the iterator at its first item. Then the Value function is invoked in order to get the value to be assigned to the variable in the for loop. This process of MoveNext/Value is repeated until the iterator returns False from MoveNext to indicate that it has no more items.
As a simple example, we’ll create a range class that works with For Each loops. We’ll start with a simple class that isn’t iterable:
Class MyRange Sub Constructor(minValue As Integer, maxValue As Integer) Self.MinValue = minValue Self.MaxValue = maxValue End SubDim MinValue As Integer Dim MaxValue As Integer End Class
Next we’ll create the iterator class:
Class MyRangeIterator Implements Xojo.Core.Iterator Sub Constructor(minValue As Integer, maxValue As Integer) mValue = minValue - 1 mMaxValue = maxValue End Sub Function MoveNext() As Boolean If mValue >= mMaxValue Then Return False mValue = mValue + 1 Return True End Function Function Value() As Auto Return mValue End Function Private Dim mValue As Integer Private Dim mMaxValue As Integer End Class
One important thing worth pointing out here is that the RangeIterator’s constructor sets mValue to the minimum value minus one. This is because iterators start out referencing the item ‘before’ the first item. This is to say that the first call to MoveNext positions the iterator at the first item, allowing MoveNext to return False to indicate that it has no items.
Finally we’ll go back and implement the Iterable interface on our Range class:
Class MyRange Implements Xojo.Core.Iterable Sub Constructor(minValue As Integer, maxValue As Integer) Self.MinValue = minValue Self.MaxValue = maxValue End Sub Function GetIterator() As Xojo.Core.Iterable Return New MyRangeIterator(MinValue, MaxValue) End Function Dim MinValue As Integer Dim MaxValue As Integer End Class
Our range class can now be used with For Each loops:
For Each i As Integer In New MyRange(100, 105) Xojo.System.DebugLog(i.ToText) Next