如何验证父项是否在递归LINQ函数中有子项?

时间:2013-12-16 17:54:07

标签: c# linq recursion

我正在做一个递归的LINQ函数,如tis问题所述: Simulating CTE recursion in C#

我的代码如下:

private static IEnumerable<KeyValuePair<int, int>> getNet(List<DataRow> list, int? leader, int level)
{
    return list
        .Where(x => x.Field<int?>("LeaderID") == leader)
        .SelectMany(x =>
            new[] { 
               new KeyValuePair<int, int>(x.Field<int>("RepID"), level)
                  }.Concat(getNet(list, x.Field<int>("RepID"), level+ 1))
         );
}

我想在再次输入该函数之前验证父母是否有孩子,因为每个孩子都会再次被评估并且会花费很多时间。

即 家长A有5000个孩子,但只有5个孩子有孩子,我需要一些东西来验证A的孩子在为所有孩子执行这个功能之前是否有孩子。

谢谢!

1 个答案:

答案 0 :(得分:3)

因此,早些时候对孩子进行测试确实无法帮助你。从概念上讲,你仍然在做同样的工作。如果每个递归调用一次处理两个深度,而不是一个,则会使方法本身变得非常复杂,只要有孩子就会复制第二个深度的工作,即使没有孩子也会获得非常非常小的工作。该方法的昂贵部分是通过子列表的线性搜索,而不是在开始搜索的方法调用中。

为了提高性能,您需要多次停止通过非常大的列表进行线性搜索。幸运的是,这很容易做到。只需在方法开始时为每个父级创建一次查找。

var lookup = list.ToLookup(x => x.Field<int?>("LeaderID"));

现在,从概念上讲,你要做的是遍历一棵树。我们可以从一个能够遍历任何树的通用“遍历”方法开始(使用非递归实现,以提高性能):

public static IEnumerable<T> Traverse<T>(IEnumerable<T> source, 
    Func<T, IEnumerable<T>> childSelector)
{
    var queue = new Queue<T>(source);
    while (queue.Any())
    {
        var item = queue.Dequeue();
        yield return item;
        foreach (var child in childSelector(item))
        {
            queue.Enqueue(child);
        }
    }
}

现在我们已经拥有了这些,我们可以使用查找来有效地获取每个节点的所有子节点,从而大大改进了实现。当然,如果你不需要每个节点的深度,它会更漂亮,但它仍然没有那么糟糕。

var roots = lookup[leader]
    .Select(row => Tuple.Create(row.Field<int>("RepID"), 0));

return Traverse(roots, node => lookup[node.Item1]
    .Select(row => Tuple.Create(row.Field<int>("RepID"), node.Item2 + 1)));

如果您不需要知道每个节点的深度,可以将所有这些简化为:

var lookup = list.ToLookup(
    row => row.Field<int?>("LeaderID"),
    row => row.Field<int>("RepID"));
return Traverse(lookup[leader], node => lookup[node]);