【问题标题】:Handling common recursive functions处理常见的递归函数
【发布时间】:2010-10-27 08:37:16
【问题描述】:

我注意到在我的项目中,我们经常编写递归函数。

我的问题是:有没有办法为每个使用递归迭代的层次结构创建递归函数作为通用函数?

也许我可以使用一个委托来获取递归的根和结束标志?

有什么想法吗?

谢谢。

【问题讨论】:

  • 您需要更加具体。可能有一种方法可以使其通用,但如果没有更多信息,我们将无能为力。
  • 您希望泛化的代码示例将在这里提供很大帮助。

标签: c# recursion hierarchy


【解决方案1】:

我认为您想要的是一种以通用方式处理层次结构的方法(“通用”在英语中定义,不一定在 .Net 中定义)。例如,这是我曾经在需要获取 Windows 窗体中的所有控件时编写的内容:

public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> selector)
{
    if (items == null)
        throw new ArgumentNullException("items");
    if (selector == null)
        throw new ArgumentNullException("selector");

    return SelectManyRecursiveInternal(items, selector);
}

private static IEnumerable<T> SelectManyRecursiveInternal<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> selector)
{
    foreach (T item in items)
    {
        yield return item;
        IEnumerable<T> subitems = selector(item);

        if (subitems != null)
        {
            foreach (T subitem in subitems.SelectManyRecursive(selector))
                yield return subitem;
        }
    }
}


// sample use, get Text from some TextBoxes in the form
var strings = form.Controls
                  .SelectManyRecursive(c => c.Controls) // all controls
                  .OfType<TextBox>() // filter by type
                  .Where(c => c.Text.StartWith("P")) // filter by text
                  .Select(c => c.Text);

另一个例子:Category 类,其中每个Category 可以有ChildCategories(同样Control 有一个Controls 集合)并假设rootCategory 直接或间接地是所有类别的父级:

// get all categories that are enabled
var categories = from c in rootCategory.SelectManyRecursive(c => c.ChildCategories)
                 where c.Enabled
                 select c;

【讨论】:

    【解决方案2】:

    我的问题是:有没有办法为每个使用递归迭代的层次结构创建递归函数作为通用函数? 也许我可以使用一个委托来获取递归的根和结束标志?

    是的 - 您唯一需要的是一个委托函数,它计算每个元素的子元素列表。当没有子节点返回时,函数终止。

        delegate IEnumerable<TNode> ChildSelector<TNode>(TNode Root);
    
        static IEnumerable<TNode> Traverse<TNode>(this TNode Root, ChildSelector<TNode> Children) {
            // Visit current node (PreOrder)
            yield return Root;
    
            // Visit children
            foreach (var Child in Children(Root)) 
                foreach (var el in Traverse(Child, Children))
                    yield return el;
    
        }
    

    例子:

            static void Main(string[] args) {
    
            var Init = // Some path
    
            var Data = Init.Traverse(Dir => Directory.GetDirectories(Dir, "*", SearchOption.TopDirectoryOnly));
    
            foreach (var Dir in Data)
                Console.WriteLine(Dir);
    
            Console.ReadKey();
        }
    

    【讨论】:

    • +1 .Net 2.0 需要委托声明。但是,如果您的目标是 .Net 3.5+,则可以简单地使用 Func>
    【解决方案3】:

    听起来您的解决方案可以成功使用Visitor Pattern

    您可以通过创建hierarchical visitor pattern 来创建Visitor Pattern 的特定变体。

    在这里完全讨论有点复杂,但这应该可以让您开始进行一些研究。基本思想是你有一个知道如何遍历结构的类,然后你有一个知道如何处理特定节点的访问者类。您可以将树的遍历与节点的处理分开。

    【讨论】:

      【解决方案4】:

      一种更简单且通用的方法可能是缓存函数的结果并仅在结果已知时使用“真实”函数 - 这种方法的有效性取决于递归期间使用同一组参数的频率.

      如果您了解 Perl,您应该查看 Higher-Order Perl 的前 4 章,这些章节可作为电子书获得,其中的想法与语言无关。

      【讨论】:

        【解决方案5】:

        我不确定您的问题到底是什么,但递归函数可以是通用的。对此没有任何限制。例如:

        int CountLinkedListNodes<T>(MyLinkedList<T> input) {
           if (input == null) return 0;
           return 1 + CountLinkedListNodes<T>(input.Next);
        }
        

        【讨论】:

          猜你喜欢
          • 2015-02-20
          • 2014-09-05
          • 2021-06-24
          • 1970-01-01
          • 2011-03-23
          • 1970-01-01
          • 2022-08-17
          • 1970-01-01
          • 2014-11-18
          相关资源
          最近更新 更多