Profiel van JonathanJonathan Chayce Dickinso...Weblog Extra Help

Weblog


    10 maart

    Trees and Anonymous Delegates

    Trees and Anonymous Delegates

    Something that I found to be really useful with the new .Net 2.0 anonymous delegates is "actioning".

    Scenario

    I had a situation where I was recursing through a tree to perform some action against all the items. Lets set up a simple tree node:

        public class TreeItem : List<TreeItem>
        {
            public string Text;
    
            public TreeItem(string text)
            {
                Text = text;
            }
        }

    The old way

    Using the old naive way we could do the following:

        static class Program
        {
            static TreeItem RootNode = new TreeItem("Root");
    
            static void Main()
            {
                // Fill the tree with useless data.
                RootNode.Add(new TreeItem("Node 1"));
                RootNode[0].Add(new TreeItem("Node 1.1"));
                RootNode[0].Add(new TreeItem("Node 1.2"));
    
                RootNode.Add(new TreeItem("Node 2"));
                RootNode[1].Add(new TreeItem("Node 2.1"));
                RootNode[1].Add(new TreeItem("Node 2.2"));
    
                // Do the actions, the old way.
                TreeItem[] items = FlattenTree();
                foreach (TreeItem item in items)
                    Console.WriteLine(item.Text);
    
                if (System.Diagnostics.Debugger.IsAttached)
                    Console.ReadLine();
            }
    
            static TreeItem[] FlattenTree()
            {
                TreeItem newItems = new TreeItem("Nothing");
    
                FlattenTree(newItems, RootNode);
    
                return newItems.ToArray();
            }
    
            private static void FlattenTree(TreeItem newItems, 
                                            TreeItem node)
            {
                newItems.Add(node);
                foreach (TreeItem item in node)
                    FlattenTree(newItems, item);
            }
        }

    As you can see, there are two loops occuring here (once to recurse the tree, and once to action all the items). That is far from optimal.

    The new way

    Let's see how we can optimize this further. Using anonymous delegates we can action all the items in the tree in a very clean fashion with little code changes required.

        static class Program
        {
            static TreeItem RootNode = new TreeItem("Root");
    
            static void Main()
            {
                // Fill the tree with useless data.
                RootNode.Add(new TreeItem("Node 1"));
                RootNode[0].Add(new TreeItem("Node 1.1"));
                RootNode[0].Add(new TreeItem("Node 1.2"));
    
                RootNode.Add(new TreeItem("Node 2"));
                RootNode[1].Add(new TreeItem("Node 2.1"));
                RootNode[1].Add(new TreeItem("Node 2.2"));
    
                // Do the actions, the new way.
                ActionTree(delegate(TreeItem item)
                {
                    Console.WriteLine(item.Text);
                });
    
                if (System.Diagnostics.Debugger.IsAttached)
                    Console.ReadLine();
            }
    
            static void ActionTree(Action<TreeItem> action)
            {
                ActionTree(action, RootNode);
            }
    
            private static void ActionTree(Action<TreeItem> action, 
                                           TreeItem node)
            {
                action(node);
                foreach (TreeItem item in node)
                    ActionTree(action, item);
            }
        }

    Now the tree is only being recursed, no iterating and assigning a useless array.

    Breaking Out the Generic Solution

    Now we need the ability to break out of the recursion. First we need to define a delegate.

        public delegate bool ContinueAction<T>(T t);

    Next we need to make the iteration process. Enter extension methods, after further tweaking I acheived the following:

        public class TreeItem : List<TreeItem>
        {
            public string Text;
    
            public TreeItem(string text)
            {
                Text = text;
            }
        }
    
        public delegate bool ContinueAction<T>(T t);
    
        static class Program
        {
            static TreeItem RootNode = new TreeItem("Root");
    
            static void Main()
            {
                // Fill the tree with useless data.
                RootNode.Add(new TreeItem("Node 1"));
                RootNode[0].Add(new TreeItem("Node 1.1"));
                RootNode[0].Add(new TreeItem("Node 1.2"));
    
                RootNode.Add(new TreeItem("Node 2"));
                RootNode[1].Add(new TreeItem("Node 2.1"));
                RootNode[1].Add(new TreeItem("Node 2.2"));
    
                // Do the actions, the generic way.
                RootNode.ActionTree<TreeItem>(delegate(TreeItem item)
                {
                    if (item.Text == "Node 2")
                        return false;
                    Console.WriteLine(item.Text);
                    return true;
                });
    
                if (System.Diagnostics.Debugger.IsAttached)
                    Console.ReadLine();
            }
    
            private static bool ActionTree<T>(this T node, ContinueAction<T> action) where T : IEnumerable<T>
            {
                if (!action(node))
                    return false;
                foreach (T item in node)
                {
                    if (!ActionTree<T>(item, action))
                        return false;
                }
                return true;
            }
        }

    And that's it! Those bound to .Net 2.0 can just remove the this from the extension method and call the method as follows:

        ActionTree<TreeItem>(RootNode, delegate(TreeItem item) {...});

    Hope this helps!!!

    Reacties

    Een ogenblik geduld...
    De reactie die je hebt ingevoerd is te lang. Maak hem iets korter.
    Je hebt niets ingevoerd. Probeer het opnieuw.
    We kunnen je reactie nu niet toevoegen. Probeer het later opnieuw.
    Je hebt toestemming van je ouders nodig om een reactie toe te voegen Toestemming vragen
    Je kunt geen reacties geven omdat je ouders dit hebben uitgeschakeld.
    We kunnen je reactie nu niet verwijderen. Probeer het later opnieuw.
    Je hebt het maximale aantal reacties overschreden dat je elke dag kunt versturen. Probeer het over 24 uur nog eens.
    De mogelijkheid om reacties te geven is uitgeschakeld voor je account omdat onze systemen aangeven dat je spam naar andere gebruikers verzendt. Als je van mening bent dat je account ten onrechte is uitgeschakeld, kun je contact opnemen met de klantondersteuning van Windows Live.
    Voer de beveiligingscontrole hieronder uit om een reactie achter te laten.
    De tekens die je typt moeten overeenkomen met die in de afbeelding of het audiofragment.

    Meld je aan bij Windows Live ID om een reactie toe te voegen (als je Hotmail, Messenger of Xbox LIVE gebruikt, heb je al een Windows Live ID). Aanmelden


    Heb je geen Windows Live ID? Maak er nu een aan

    Links naar je weblog

    De URL voor de link naar dit weblogitem is:
    http://chayce-za.spaces.live.com/blog/cns!B69C6138F2FF2771!194.trak
    Weblogs die naar dit item verwijzen
    • Geen
    *