Die Community zu .NET und Classic VB.
Menü

VB.NET-Tipp 0104: Baumstrukturen durchlaufen mit rekursivem Lambda-Ausdruck

 von 

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:

Schwierigkeitsgrad 2

Framework-Version(en):

.NET Framework 3.5

.NET-Version(en):

Visual Basic 2008

Download:

Download des Beispielprojektes [14,42 KB]

' 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.