12 Jul 2012 @ 4:40 AM 

Breaking completely from my usual programming topics, which surround C# most of the time, today I will be looking at Some VB.NET, and, more generally, the topic of iterator methods. For quite some time, Visual Basic.NET has been something of the younger brother to C#; it usually got features long after C# did. Iterator methods are no exception.

What is an “Iterator Method” Anyway?

An iterator method is a coroutine. The best way to describe it is to see it in action. Take the following C# Code as an example:

  1.  
  2. public class testprogram
  3. {
  4.     public IEnumerable<int> SomeRange(int minvalue,int maxvalue)
  5.     {
  6.         for (int i=minvalue;i<maxvalue;i++)
  7.             yield return i;
  8.  
  9.  
  10.     }
  11.  
  12.     public static void Main(String []  args)
  13.     {
  14.         foreach(int value in SomeRange(0,50))
  15.         {
  16.             if(value==25) break;
  17.             Console.WriteLine(value);
  18.  
  19.         }
  20.  
  21.     }
  22.  
  23. }
  24.  

In this example, the code would output the values 0 through 24. The difference between this and say something like this:

  1.  
  2. public IEnumerable<int> SomeRange(int minvalue,int maxvalue)
  3. {
  4.     List<int> createlist = new List<int>();
  5.     for(int i=minvalue;i<maxvalue;i++)
  6.        createlist.Add(i);
  7.    
  8.     return createlist;
  9.  
  10.  
  11. }
  12.  

Is one of both semantics as well as performance. In the first case, the iterator routine is only executed until the next yield, or the iterator routine returns. At which point, either the next element will be passed through the foreach body or the foreach loop will be finished. So, for example, the iterator version of SomeRange() will not iterate past 24 in this example, but the latter does, since it constructs the entire list, and that resulting list is then dealt with using the list enumerator.

One particularly useful purpose is for endless sequences; the second method is impossible for this, since you cannot simply fill a list with an infinite sequence. The iterator routine pattern allows you to define a sequence based on a larger code block by yielding specific values to the enumerating routine. Many other languages have support for the concept of iterator functions. Python, for example, has them, and they work very similarly:

  1.  
  2. #!/usr/bin/python
  3. def itertest():
  4.     x=0
  5.     while(True):
  6.         x=x+1
  7.         yield x
  8.  
  9.  
  10. for x in itertest():
  11.     print(x)
  12.     if x > 50:
  13.         break
  14.  

In that example, itertest() is a “generator” or iterator function, just like the C# Example above. Because Python is a strongly-typed dynamic language, it has very few special things you need to do; basically, all you have to do is use yield instead of return. the C# Example has to use yield return, as well as having the function signature return a IEnumerable. C++ has a concept of iterator classes, which doesn’t make the syntax simple and “language-defined” but provides a well-defined set of abstract classes that can be relied on.

Visual Basic 6

Visual Basic 6 is an interesting case. The language itself is quite limited. Of course it has absolutely no concept of iterators; it’s For…Each loop acts on an IEnumVariant interface, but you cannot even implement this interface yourself easily. Implementing IEnumVariant in VB6 is possible, but it requires a lot of manual hacking of virtual call tables to point to module-level functions, exacerbated by the fact that the IEnumVariant Interface has method names that are Visual Basic 6 reserved words.

Visual Basic .NET

Visual Basic .NET has been a bit better off than VB6 since inception; it doesn’t directly support iterator methods/coroutines yet (it will in VB10) But you can at least implement the IEnumerator interface. You do lose the ability to have the nice iterator syntax that C# and Python have, though.

Faking it

It is, however, possible to sort of fake it with VB.NET, by creating your own implementation of an enumerator that accepts a delegate; that delegate is passed a single argument- the iterator object- which has two methods- Yield, and Break. Yield returns the next value in the sequence, and Break cancels the iteration. Note that the implementation I came up with does not work like the versions in either Python or C#; in both those cases, the compiler/interpreter turns the iterator function into a state machine. My implementation uses a threaded model- the MoveNext() routine waits until Yield or Break is called before returning. Here is the implementation I came up with. Bear in mind I don’t usually work with VB.NET…

  1.  
  2.    Public Class Iterator(Of T)
  3.         Implements IEnumerable(Of T)
  4.         Implements IEnumerator(Of T)
  5.  
  6.         Public Delegate Sub IteratorRoutine(ByVal ih As Iterator(Of T))
  7.      
  8.         Private Coroutine As Thread
  9.         Private CurrentValue As T
  10.         Private hasNext As Boolean
  11.         Private IterationCompleted As Boolean
  12.         Private CalledUs As Boolean
  13.  
  14.  
  15.         ‘Yields the given value
  16.         Public Function Yield(ByVal YieldValue As T) As T
  17.             Yield = YieldValue
  18.             Static InCall As Object
  19.             If InCall Is Nothing Then InCall = New Object()
  20.             SyncLock (InCall)
  21.  
  22.                 SyncLock (Me)
  23.                     hasNext = True
  24.                     CurrentValue = YieldValue
  25.                     CalledUs = True
  26.                 End SyncLock
  27.             End SyncLock
  28.             InCall = Nothing
  29.         End Function
  30.         Public Sub Break()
  31.             SyncLock (Me)
  32.                 IterationCompleted = True
  33.                 CalledUs = True
  34.             End SyncLock
  35.         End Sub
  36.  
  37.         Public Sub Dispose() Implements IDisposable.Dispose
  38.             If Not Coroutine Is Nothing Then
  39.                 Coroutine.Abort()
  40.             End If
  41.  
  42.         End Sub
  43.         Public Sub Coroutinerunner(ByVal parameter As Object)
  44.             Try
  45.                 CalledUs = True
  46.                 Do While Not IterationCompleted AndAlso CalledUs
  47.                     CalledUs = False
  48.                     hasNext = False
  49.                     _iterator(Me)
  50.                     If Not CalledUs Then IterationCompleted = True
  51.                     Do
  52.  
  53.                         SyncLock (Me)
  54.                             If Not hasNext AndAlso Not IterationCompleted Then Exit Do
  55.  
  56.  
  57.                         End SyncLock
  58.                         Thread.Sleep(0)
  59.                     Loop
  60.  
  61.                 Loop
  62.             Catch exx As ThreadAbortException
  63.  
  64.  
  65.             End Try
  66.  
  67.         End Sub
  68.         Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
  69.             CalledUs = False
  70.             hasNext = False
  71.  
  72.             If Coroutine Is Nothing Then
  73.                 ‘we need to start the thread.
  74.                 Coroutine = New Thread(AddressOf Coroutinerunner)
  75.                 Coroutine.Start()
  76.  
  77.             End If
  78.             Do
  79.                 SyncLock (Me)
  80.                     If (hasNext OrElse IterationCompleted) Then Exit Do
  81.                 End SyncLock
  82.                 Thread.Sleep(0)
  83.  
  84.             Loop
  85.             If hasNext Then Return True Else Return False
  86.  
  87.         End Function
  88.  
  89.         Public Sub Reset() Implements IEnumerator.Reset
  90.             Throw New NotImplementedException()
  91.         End Sub
  92.  
  93.         Public ReadOnly Property IEnumerator_Current() As T Implements IEnumerator(Of T).Current
  94.             Get
  95.                 Return CurrentValue
  96.             End Get
  97.         End Property
  98.  
  99.         Public ReadOnly Property Current() As Object Implements IEnumerator.Current
  100.             Get
  101.                 Return CurrentValue
  102.             End Get
  103.         End Property
  104.  
  105.  
  106.         Private _iterator As Iterator(Of T).IteratorRoutine
  107.  
  108.         Public Sub New(ByVal iterator As Iterator(Of T).IteratorRoutine)
  109.             _iterator = iterator
  110.  
  111.         End Sub
  112.  
  113.  
  114.         Public Function IEnumerable_GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
  115.             Return Me
  116.         End Function
  117.  
  118.         Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
  119.             Return Me
  120.         End Function
  121.     End Class
  122.  

Usage of this class is relatively simple, compared to having to write your own full blown Enumerable implementation. First, you need a method satisfying the delegate:

  1.  
  2.   Sub testIterator(ByVal ih As Iterator(Of Double))
  3.         Dim y As Double
  4.         For y = 0 To 10 Step 1
  5.             If y > 5 Then Thread.Sleep(500)
  6.             ih.Yield(y)
  7.             If (y > 8) Then ih.Break()
  8.             Debug.Print("Returning " & y)
  9.         Next
  10.  
  11.     End Sub
  12.  

Note the use of ih.Yield() and ih.Break(), which “emulate” the appropriate statements from C# (Or Python, for that matter). Using it would look like this:

 
  Sub Main()


        Dim iterate As Double

        For Each iterate In New Iterator(Of Double)(AddressOf testIterator)


            Console.WriteLine(iterate)

        Next




        Console.ReadKey()

    End Sub
 

It’s not as succint as the C# version but still a lot shorter.

On the horizon

Thankfully VB10 will remove this goofy problem- it adds support for iterator methods. Though it’s still a bit goofy, it does allow some things C# doesn’t such as anonymous iterator methods.

546 total views, no views today

Posted By: BC_Programming
Last Edit: 12 Jul 2012 @ 04:40 AM

EmailPermalinkComments (0)
Tags
Categories: Programming

 Last 50 Posts
 Back
Change Theme...
  • Users » 856
  • Posts/Pages » 191
  • Comments » 67
Change Theme...
  • VoidVoid « Default
  • LifeLife
  • EarthEarth
  • WindWind
  • WaterWater
  • FireFire
  • LightLight

PP



    No Child Pages.

Windows optimization tips



    No Child Pages.