【问题标题】:Recursive lambda expression to traverse a tree in C#递归 lambda 表达式在 C# 中遍历树
【发布时间】:2010-09-08 20:03:57
【问题描述】:

谁能告诉我如何实现一个递归 lambda 表达式来遍历 C# 中的树结构。

【问题讨论】:

    标签: c# recursion lambda


    【解决方案1】:

    假设一个神话对象 TreeItem,它包含一个 Children 集合来表示您的层次结构。

        public void HandleTreeItems(Action<TreeItem> item, TreeItem parent)
        {
            if (parent.Children.Count > 0)
            {
                foreach (TreeItem ti in parent.Children)
                {
                    HandleTreeItems(item, ti);
                }
            }
    
            item(parent);
        }
    

    现在调用它,传入处理一项的 lambda,将其名称打印到控制台。

    HandleTreeItems(item => { Console.WriteLine(item.Name); }, TreeItemRoot);
    

    【讨论】:

    • HandleTreeItems 不是 lambda,它只是将 lambda 作为参数。因此,这并不能回答具体问题。
    【解决方案2】:

    一个合适的解决方案,实际上是许多函数式编程语言中的惯用解决方案,是使用fixed-point combinator。简而言之:定点组合器回答了“如何将匿名函数定义为递归的?”的问题。但是解决方案是如此重要,以至于写了整篇文章来解释它们。

    一个简单实用的替代方法是“回到过去”到 C 的滑稽动作:定义之前的声明。尝试以下(“阶乘”函数):

    Func<int, int> fact = null;
    fact = x => (x == 0) ? 1 : x * fact(x - 1);
    

    像魅力一样工作。

    或者,对于类 TreeNode 的对象的预排序树遍历,该对象适当地实现 IEnumerable&lt;TreeNode&gt; 以遍历其子对象:

    Action<TreeNode, Action<TreeNode>> preorderTraverse = null;
    preorderTraverse = (node, action) => {
        action(node);
        foreach (var child in node) preorderTraverse(child, action);
    };
    

    【讨论】:

    • 出于好奇,这个阶乘函数如何帮助回答给定的问题?
    • 因为它是递归的
    • 如果解决方案位于通常认为的递归函数中,其他用户更容易理解解决方案,而不是必须了解 OP 的具体问题。
    【解决方案3】:

    一个简单的替代方法是“回到过去”到 C 和 C++ 的滑稽动作:在定义之前声明。请尝试以下操作:

    Func<int, int> fact = null;
    fact = x => (x == 0) ? 1 : x * fact(x - 1);
    

    像魅力一样工作。

    是的,这确实有效,但有一点需要注意。 C# 具有可变引用。所以请确保你不会意外地做这样的事情:

    Func<int, int> fact = null;
    fact = x => (x == 0) ? 1 : x * fact(x - 1);
    
    // Make a new reference to the factorial function
    Func<int, int> myFact = fact;
    
    // Use the new reference to calculate the factorial of 4
    myFact(4); // returns 24
    
    // Modify the old reference
    fact = x => x;
    
    // Again, use the new reference to calculate
    myFact(4); // returns 12
    

    当然,这个例子有点做作,但是在使用可变引用时可能会发生这种情况。如果您使用来自aku 链接的组合器,这是不可能的。

    【讨论】:

      【解决方案4】:

      好的,我终于找到了一些空闲时间。
      我们开始吧:

      class TreeNode
      {
          public string Value { get; set;}
          public List<TreeNode> Nodes { get; set;}
      
      
          public TreeNode()
          {
              Nodes = new List<TreeNode>();
          }
      }
      
      Action<TreeNode> traverse = null;
      
      traverse = (n) => { Console.WriteLine(n.Value); n.Nodes.ForEach(traverse);};
      
      var root = new TreeNode { Value = "Root" };
      root.Nodes.Add(new TreeNode { Value = "ChildA"} );
      root.Nodes[0].Nodes.Add(new TreeNode { Value = "ChildA1" });
      root.Nodes[0].Nodes.Add(new TreeNode { Value = "ChildA2" });
      root.Nodes.Add(new TreeNode { Value = "ChildB"} );
      root.Nodes[1].Nodes.Add(new TreeNode { Value = "ChildB1" });
      root.Nodes[1].Nodes.Add(new TreeNode { Value = "ChildB2" });
      
      traverse(root);
      

      【讨论】:

      • IIRC 框架中不存在 ForEach 扩展方法,所以你必须自己编写它(这很容易)。
      猜你喜欢
      • 2018-07-12
      • 2014-03-11
      • 1970-01-01
      • 1970-01-01
      • 2017-05-11
      • 2021-05-04
      • 2018-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多