VB.NET-Tipp 0095: IEnumerable implementieren - Array permutieren
von Spatzenkanonier
Beschreibung
Die Member der Schnittstellen IEnumerable und IEnumerator sind Basis aller For Each-Schleifen. Üblicherweise werden diese Schnittstellen von Auflistungen implementiert, man kann sie aber auch selbst implementieren und Code hinterlegen, der mit Auflistungen nichts mehr gemein hat.
Die Klasse Permutations(Of T) etwa tritt nach außen als Auflistung auf, die alle Permutationen eines Arrays enthält. Tatsächlich wird die nächste Permutation aber erst berechnet, wenn der Enumerator vorrückt.
Über diesen Tipp hinausgehend finden sich im Artikel Testlauf - Zeichenfolgenpermutation des MSDN Magazins weitere Informationen zur Problemstellung, etwa ein überaus listiges Verfahren zur direkten Berechnung der n-ten Permutation ohne alle Vorgänger durchzugehen zu müssen.
Schwierigkeitsgrad: | Framework-Version(en): .NET Framework 2.0, .NET Framework 3.0, .NET Framework 3.5 | .NET-Version(en): Visual Basic 2005, Visual Basic 2008 | Download: |
' Dieser Quellcode stammt von http://www.activevb.de ' und kann frei verwendet werden. Für eventuelle Schäden ' wird nicht gehaftet. ' Um Fehler oder Fragen zu klären, nutzen Sie bitte unser Forum. ' Ansonsten viel Spaß und Erfolg mit diesem Source! ' Projektversion: Visual Studio 2005 ' Option Strict: An ' ' Referenzen: ' - System ' - System.Data ' - System.Xml ' ' Imports: ' - Microsoft.VisualBasic ' - Microsoft.VisualBasic.ControlChars ' - System ' - System.Collections ' - System.Collections.Generic ' - System.Data ' - System.Diagnostics ' ' ############################################################################## ' ################################ modMain.vb ################################## ' ############################################################################## Public Module modMain Public Sub Main() ' Einfacher kann man wohl nicht permutieren For Each Mutation As String() In _ Permutations.Of("ant", "bat", "cow", "dog") Console.WriteLine(String.Join(" ", Mutation)) Next Console.ReadLine() End Sub End Module ' ############################################################################## ' ############################## Permutations.vb ############################### ' ############################################################################## Imports System.Collections Public Class Permutations ''' <summary> ''' Ermittelt durch Rückschluss den Type-Parameter, sodass man ihn nicht mal ''' anzugeben braucht ''' </summary> Public Shared Function [Of](Of T)( _ ByVal ParamArray InitArray As T()) As Permutations(Of T) Return New Permutations(Of T)(InitArray) End Function End Class 'Permutations ''' <summary> ''' erzeugt einen Enumerator zur Permutation von Arrays beliebigen Types, ''' verwendbar in ForEach-Schleifen ''' </summary> Public Class Permutations(Of T) : Implements IEnumerable Private _InitArray As T() Public Sub New(ByVal InitArray As T()) _InitArray = InitArray End Sub Public Function GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return New Enumerator(_InitArray) End Function Public Class Enumerator : Implements IEnumerator ' Das Implementieren von IEnumerable zieht direkt ein Implementieren ' von IEnumerator nach sich; ist IEnumerable doch nix anderes als eine ' "factory" für Enumeratoren Private _InitArray As T() ' Die Indizes werden permutiert Private _Indices As Integer() Friend Sub New(ByVal InitArray As T()) _InitArray = InitArray End Sub Public Sub Reset() Implements IEnumerator.Reset ' Eigentümlicherweise wird dieser Schnittstellenmember nie verwendet End Sub Public ReadOnly Property Current() As Object _ Implements IEnumerator.Current Get Dim UBound As Integer = _InitArray.Length - 1 Dim RetVal(UBound) As T '_InitArray mit umgestellter Reihenfolge nach RetVal kopieren For I As Integer = 0 To UBound RetVal(I) = _InitArray(_Indices(I)) Next Return RetVal End Get End Property ''' <summary> ''' Berechntet aus der vorherigen Permutation die nächste ''' </summary> ''' <remarks>Die neue Permutation ist als Current() abrufbar</remarks> Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext Dim UBound As Integer = _InitArray.Length - 1 If _Indices Is Nothing Then ' Indizes-Startwerte: 0,1,2,3,4... ReDim _Indices(UBound) For I As Integer = 0 To UBound _Indices(I) = I Next ' Mehr ist beim Erstaufruf nicht zu tun Return UBound > 0 End If ' Indicees permutieren. ' Es wird ein linker und ein rechter Wert gesucht, die vertauscht ' werden. Anschließend die Reihenfolge rechts vom Links-Wert ' umkehren Dim Left As Integer, Right As Integer ' Links-Wert: muss kleiner sein als sein rechter Nachbar For Left = UBound - 1 To 0 Step -1 If _Indices(Left) < _Indices(Left + 1) Then Exit For Next If Left < 0 Then ' Eine weitere Permutation kann nicht berechnet werden _Indices = Nothing '_Indices rücksetzen Return False End If Dim LeftVal As Integer = _Indices(Left) ' Rechts-Wert: größer als der Links-Wert ' (ist nicht immer o.g. Nachbar!) For Right = UBound To 0 Step -1 If _Indices(Right) > LeftVal Then Exit For Next ' Links-Wert und Rechts-Wert tauschen _Indices(Left) = _Indices(Right) _Indices(Right) = LeftVal ' Array rechts der Links-Position umkehren Array.Reverse(_Indices, Left + 1, UBound - Left) ' WriteLine(_Indices) ' Test-Ausgabe Return True End Function 'MoveNext End Class 'Permutations(Of T).Enumerator End Class 'Permutations(Of T)
Ihre Meinung
Falls Sie Fragen zu diesem Artikel haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.