C# 迭代块到LINQ

C# 迭代块到LINQ,c#,linq,iterator,yield-return,C#,Linq,Iterator,Yield Return,我很难找到用于以下迭代器块的正确LINQ语法: class Program { class Operation { public IEnumerable<Operation> NextOperations { get; private set; } } class Item { } static Item GetItem(Operation operation) { return new Item();

我很难找到用于以下迭代器块的正确LINQ语法:

class Program
{
    class Operation
    {
        public IEnumerable<Operation> NextOperations { get; private set; }
    }
    class Item { }

    static Item GetItem(Operation operation)
    {
        return new Item();
    }

    static IEnumerable<Item> GetItems(IEnumerable<Operation> operations)
    {
        foreach (var operation in operations)
        {
            yield return GetItem(operation);

            foreach (var item in GetItems(operation.NextOperations))  // recursive
                yield return item;
        }
    }

    static void Main(string[] args)
    {
        var operations = new List<Operation>();
        foreach (var item in GetItems(operations))
        {
        }
    }
}
类程序
{
班级作业
{
公共IEnumerable下一个操作{get;private set;}
}
类项{}
静态项GetItem(操作)
{
返回新项目();
}
静态IEnumerable GetItems(IEnumerable操作)
{
foreach(操作中的var操作)
{
收益返回项(操作);
foreach(GetItems中的var项(operation.nextoOperations))//递归
收益回报项目;
}
}
静态void Main(字符串[]参数)
{
var operations=新列表();
foreach(GetItems中的var项(操作))
{
}
}
}

也许我所拥有的和它所拥有的一样好?对于这个特定的代码,在显式的foreach中使用yield return确实是正确的解决方案?

我认为您的实现是好的。但是,如果您想使用LINQ(并使其稍微短一点,但不明显短一点),则可以使用一个查询来实现
GetItems
,该查询迭代所有操作并返回当前项,然后返回所有其他递归生成的项:

static IEnumerable<Item> GetItems(IEnumerable<Operation> operations) 
{ 
    return from op in operations
           from itm in (new[] { GetItem(op) }).Concat
                       (GetItems(op.NextOperations));
           select itm;
} 

正如Jon所提到的,使用query编写递归方面没有好方法(可以使用lambda函数而不是方法编写递归查询,但这也不是更好的方法)。

我认为您的实现很好。但是,如果您想使用LINQ(并使其稍微短一点,但不明显短一点),则可以使用一个查询来实现
GetItems
,该查询迭代所有操作并返回当前项,然后返回所有其他递归生成的项:

static IEnumerable<Item> GetItems(IEnumerable<Operation> operations) 
{ 
    return from op in operations
           from itm in (new[] { GetItem(op) }).Concat
                       (GetItems(op.NextOperations));
           select itm;
} 

正如Jon所提到的,使用query编写递归方面没有好方法(可以使用lambda函数而不是方法编写递归查询-但这也不是更好的方法)。

LINQ通常不擅长使用标准查询运算符进行递归。您可以编写更通用的上述形式,但找不到一种简洁的标准LINQ方法来进行遍历。

LINQ通常不擅长使用标准查询运算符进行递归。您可以编写上述更通用的表单,但找不到一种简洁的标准LINQ方法来进行遍历

也许我所拥有的和它所拥有的一样好

非常好。我们可以让它稍微好一点

对于这个特定的代码,显式foreach内部的yield-return确实是正确的解决方案吗

这是一个合理的解决方案。它很容易阅读,也很清楚正确。正如我前面提到的,不利的一面是,如果树非常深,那么性能可能不好

我会这样做:

static IEnumerable<T> AllNodes(this T root, Func<T, IEnumerable<T>> getChildren) 
{
    var stack = new Stack<T>();
    stack.Push(root);
    while(stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;
        foreach(var child in getChildren(current).Reverse())
            stack.Push(child);
    }
} 

static void Main()      
{      
    var operation = whatever;
    var items = from op in operation.AllNodes(x=>x.NextOperations)
                select GetItem(op);
    foreach (var item in items)      
    {      
    }      
} 
现在堆栈是空的,所以我们完成了,我们以“preorder遍历”的顺序获取项目。如果你不关心顺序,如果你需要的是确保你得到了所有这些,那么就不用费心去颠倒孩子们的顺序,你会得到他们的顺序是Alpha,Delta,Omega,Zeta,Gamma,Beta

有道理吗

也许我所拥有的和它所拥有的一样好

非常好。我们可以让它稍微好一点

对于这个特定的代码,显式foreach内部的yield-return确实是正确的解决方案吗

这是一个合理的解决方案。它很容易阅读,也很清楚正确。正如我前面提到的,不利的一面是,如果树非常深,那么性能可能不好

我会这样做:

static IEnumerable<T> AllNodes(this T root, Func<T, IEnumerable<T>> getChildren) 
{
    var stack = new Stack<T>();
    stack.Push(root);
    while(stack.Count > 0)
    {
        var current = stack.Pop();
        yield return current;
        foreach(var child in getChildren(current).Reverse())
            stack.Push(child);
    }
} 

static void Main()      
{      
    var operation = whatever;
    var items = from op in operation.AllNodes(x=>x.NextOperations)
                select GetItem(op);
    foreach (var item in items)      
    {      
    }      
} 
现在堆栈是空的,所以我们完成了,我们以“preorder遍历”的顺序获取项目。如果你不关心顺序,如果你需要的是确保你得到了所有这些,那么就不用费心去颠倒孩子们的顺序,你会得到他们的顺序是Alpha,Delta,Omega,Zeta,Gamma,Beta


有意义吗?

有一些更奇特的方法可以将序列压缩在一起,但我不确定它们是否是一种改进。这并不是正常意义上的压缩。这更像是一个树遍历。@recursive:我认为你是对的,但我也认为这可以被视为一个奇怪的、特殊的压缩情况。注意,这个递归解在时间上是O(nh),其中h是树的高度;因为树的高度介于lgn和n之间,所以它介于O(nlgn)和O(n2)之间。如果树很深,这可能会有非常糟糕的性能。如果你用显式堆栈管理编写一个非递归的解决方案,那么它在时间上变成O(n),在空间上变成O(h)。有更奇特的方法将序列压缩在一起,但我不确定它们是否是一种改进。这并不是正常意义上的压缩。这更像是一个树遍历。@recursive:我认为你是对的,但我也认为这可以被视为一个奇怪的、特殊的压缩情况。注意,这个递归解在时间上是O(nh),其中h是树的高度;因为树的高度介于lgn和n之间,所以它介于O(nlgn)和O(n2)之间。如果树很深,这可能会有非常糟糕的性能。如果你用显式堆栈管理编写一个非递归的解决方案,那么它在时间上变成O(n),在空间上变成O(h)。那么,你认为托马斯的答案如何?@Steven:很好,但它仍然需要递归部分。你不能只在一条语句中完成这一切,没有额外的方法和变量来表示递归使用的操作。或者,根据Lippert的分析,使用一个带有显式堆栈的额外变量来代替递归。有趣的东西。那么,你觉得托马斯的答案怎么样?@Steven:很好,但它仍然需要递归部分。你不能只在一条语句中完成这一切,没有额外的方法和变量来表示递归使用的操作。或者,根据Lippert的分析,使用一个带有显式堆栈的额外变量来代替递归。有趣的东西。这里有基本情况吗?看来会结束的