VB.NET-Tipp 0104: Baumstrukturen durchlaufen mit rekursivem Lambda-Ausdruck
von Spatzenkanonier
Beschreibung
Mit dem hier vorgestellten rekursiven generischen Lambda-Ausdruck kann man beliebige rekursive Datenstrukturen enumerieren ohne jeweils eine rekursive Funktion zu implementieren - eine Generatorfunktion für die jeweiligen Kindknoten genügt. Die Enumerationen kann man bequem in For-Each-Schleifen durchlaufen, was zum Beispiel die Suche in Bäumen erheblich vereinfachen kann. Auch kann man die Enumeration in eine List(Of T) umwandeln und direkt als DataSource an eine Listbox binden. Zu beachten ist, dass die Reihenfolge des Durchganges sehr von der einer üblichen Rekursion abweicht.
Schwierigkeitsgrad: | Framework-Version(en): .NET Framework 3.5 | .NET-Version(en): 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 2008 ' Option Strict: An ' Option Infer: An ' ' Referenzen: ' - System ' - System.Core ' - System.Data ' - System.Deployment ' - System.Drawing ' - System.Windows.Forms ' - System.Xml ' ' Imports: ' - Microsoft.VisualBasic ' - Microsoft.VisualBasic.ControlChars ' - System ' - System.Collections.Generic ' - System.Data ' - System.Diagnostics ' - System.Linq ' - System.Windows.Forms ' ' ############################################################################## ' ########################### frmRecursiveLambda.vb ############################ ' ############################################################################## Imports System.Collections Public Class frmRecursiveLambda Private Sub frmRecursiveLambda_Load(ByVal sender As Object, _ ByVal e As EventArgs) Handles MyBase.Load Me.TreeView1.ExpandAll() End Sub Private Sub Button_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles btFolders.Click, btContrls.Click, _ btTreenodesLambda.Click, btRecurseClassic.Click Select Case True ' Dateisystem enumerieren Case sender Is btFolders Dim Root = IO.Path.GetFullPath("..\..") Dim GetChildren As Func(Of String, IEnumerable) = _ Function(S) IO.Directory.GetDirectories(S) Me.ListBox1.DataSource = GetChildren.All(Root).ToList ' Alle Controls dieses Forms enumerieren Case sender Is btContrls Dim GetChildren As Func(Of Control, IEnumerable) = _ Function(C) C.Controls ' Für die Anzeige ist ein Select angehängt, der die Namen der ' Controls selektiert Me.ListBox1.DataSource = _ GetChildren.All(Me).Select(Function(C) C.Name).ToList ' Alle Treenodes enumerieren Case sender Is btTreenodesLambda ' Beachte die eigenwillige Reihenfolge der ListItems Dim GetChildren As Func(Of TreeNode, IEnumerable) = _ Function(Nd) Nd.Nodes ' Hier wird für die Anzeige Treenode.FullPath selektiert Me.ListBox1.DataSource = _ GetChildren.AllChildren(Me.TreeView1.Nodes) _ .Select(Function(Nd) Nd.FullPath).ToList ' klassische Rekursion - "Depth first" Case sender Is btRecurseClassic Me.ListBox1.DataSource = Nothing CollectDepthFirst(Me.TreeView1.Nodes) End Select End Sub Private Sub CollectDepthFirst(ByVal Nodes As TreeNodeCollection) ' Klassische rekursive Methode - für jeden Bedarf muss man eine eigene ' Methode schreiben For Each Nd As TreeNode In Nodes Me.ListBox1.Items.Add(Nd.FullPath) CollectDepthFirst(Nd.Nodes) Next End Sub End Class ' ############################################################################## ' ############################# modExtensions.vb ############################### ' ############################################################################## Imports System.Runtime.CompilerServices Imports System.Collections Public Module modExtensions <Extension()> _ Public Function AllChildren(Of T)( _ ByVal GetChildren As Func(Of T, IEnumerable), _ ByVal Roots As IEnumerable) As IEnumerable(Of T) Dim Enumerate As Func(Of IEnumerable(Of T), IEnumerable(Of T)) = Nothing Enumerate = Function(Nd) Nd.Concat(Nd.SelectMany(Function(Nd2) _ Enumerate(GetChildren(Nd2).Cast(Of T)))) Return Enumerate(Roots.Cast(Of T)) End Function <Extension()> _ Public Function All(Of T)(ByVal GetChildren As Func(Of T, IEnumerable), _ ByVal Root As T) As IEnumerable(Of T) ' Root wird in die Enumeration hineingenommen, indem ein es ' enthaltendes IEnumerable(Of T) geschaffen wird, dessen Kinder ' (= nur Root) rekursiv enumeriert werden Return GetChildren.AllChildren(New T() {Root}) End Function End Module
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.