Visual Studio VS2012 (VB11) introduced to the vb.net language the the ability to write Iterators.
With Iterator Function.
IEnumerable(Of T) Interface
To understand what an iterator is lets look at the return type an iterator has to be IEnumerable(Of ... ).
Interface IEnumerable(Of T) Public Function GetEnumerator() As IEnumerator(Of T) Public Function GetEnumerator1() As IEnumerator End Interface
Not much to it basically it state that the object can be enumerated over. Think as enumerated as it produces a sequence of values. So this indicates that there needs to be a generic interface for producing that sequence.
IEnumerator(Of T)
Interface IEnumerator(Of T) Function MoveNext() As Boolean Sub Reset() ReadOnly Property Current As T ReadOnly Property Currect1 As Object Overridable Sub Dispose(disposing As Boolean) End Interface
The core parts of the interface are the following method and property.
Interface IEnumerator(Of T) Function MoveNext() As Boolean ReadOnly Property Current As T End Interface
MoveNext() As Boolean this return false if there isn't another value to produce, and returns true if there is. Important this also advance the value (current) to the next value.
Current As T this is the current value of the enumerator.
We tend to use in conjunction with IEnumerator the construct ForEach i In en
But this sort of get rewritten as a [i]While Loop[/il]
While en.MoveNext() Dim i = en.Current Console.WriteLine( i ) End While
Iterator
How does the Iterator Function help?
It helps by letting us write enumerators in a more convenient form and the compiler handles the task of converting into a enumerator. That enumerator can be very complex indeed, think VB.net's lets you use try ... catch inside it.
OK let implement an iterator, I'll pick to reimplement Enumerable.Take because it show what work the compiler does on your behalf.
<Extension> Iterator Function Take(Of T)( source As IEnumerable(of T), count As Integer) As IEnumerable(Of T)
Dim en = source.GetEnumerator
While en.MoveNext AndAlso count>0
Yield en.Current
count-=1
End While
End Function
That's look relatively simple, doesn't it.
But remember that IEnumerator only work via essentially two methods MoveNext and Current.
So it needs to implemented via these two methods.
Public Class TakeEnumerator(Of T)
Implements IEnumerator(Of T)
Dim _Source As IEnumerator(Of T)
Dim _Count As Integer = 0
Dim _Limit As Integer
Dim _Value As T
Public Sub new (source As IEnumerable(OF T), Count As Integer)
_Source = source.GetEnumerator
_Limit =Count
End Sub
Public ReadOnly Property Current As T Implements IEnumerator(Of T).Current
Get
Return _Value
End Get
End Property
Public ReadOnly Property Current1 As Object Implements IEnumerator.Current
Get
Return Current
End Get
End Property
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
If (_Count < _Limit) OrElse (Not _Source.MoveNext) then Return False
_Value =_Source.Current
_Count +=1
Return True
End Function
Public Sub Reset() Implements IEnumerator.Reset
_Source.Reset
_Count = _Limit
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: dispose managed state (managed objects).
End If
' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
' Dispose(False)
' MyBase.Finalize()
'End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Usage:
Dim n=New TakeEnumerator(Of Integer)(ProduceRandomIntegerSequence(0,100),Count:= 5)
' The following is essential what ForEach does.
While n.MoveNext
Console.WriteLine(n.Current)
End While
Which do you think is easier to write? The iterator version or the IEnumerator?
Spoiler
Examples
Iterator Function OneToHundred() As IEnumerable(Of Integer)
For i=1 To 100
Yield i
Next
End Function
Spoiler
.To
<Extension>
Public Iterator Function To( f As Integer, t As Integer ) As IEnumerable(Of Integer)
Dim s = If( f>t, -1,1)
For i = f To t Step s
Yield i
Next
End Function
.Select
<Extension>
Public Iterator Function Select(Of T, TResult) ( Source As IEnumerable(Of T),
Transform As Func(Of T,TResult) ) As IEnumerable(Of TResult)
For Each Item In Source
Yield Transform( Item )
Next
End Function
.Map
<Extension>
Public Iterator Function Map(Of T) ( Source As IEnumerable(Of T),
Pred As Func(OF T, Boolean)) As IEnumerable(Of T)
For Each item In Source
If Pred( item ) Then Yield item
Next
End Function
.Fold
<Extension>
Function Fold(Of T, TResult)( Source As IEnumerable(Of T), seed As TResult, FoldFN As Func(Of TResult, T, TResult) ) As IEnumerable(OF TResult)
Dim Result As TResult = Seed
For Each Item In Source
Result = FoldFn( Result, Item )
Next
Return Result
End Function
Do those iterators seem similar to existing methods?
Spoiler
Infinite Length Iterators
You have to be aware that it allows use write and Enumerator that has an infinite length
<Extension>
Iterator Function ProduceRandomIntegerSequence( Between As Integer, _And_ As Integer) As IEnumerable(Of Integer)
While True
Yield RNG.Next(Between, _And_ ) ' RNG is a System.Random
End While
End Function
So be aware if you do something that goes through the sequence, for example .Select or .Where that it'll take forever. Or do something that'll us a finite subset of it first. eg .Take(10)
Examples of Mathematical Sequences Iterators
Public Iterator Function N0() As IEnumerable(Of Integer)
For i = 0 To Integer.MaxValue
Yield i
Next
End Function
Public Function N1() As IEnumerable(Of Integer)
Return N0.Skip(1)
End Function
Public Iterator Function Fib() As IEnumerable(of BigInteger)
Dim q As New Queue(Of BigInteger)({BigInteger.Zero,BigInteger.One})
While True
q.Enqueue(q(0)+q(1))
Yield q.Dequeue
End While
End Function
If you are feeling adventurous implement the enumerator implementations of these examples (without using iterator of cause).





MultiQuote

|