LINQ 的迭代器块
我很难找到正确的 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))
{
}
}
}
也许我所拥有的已经尽善尽美了?对于这个特定的代码,显式 foreach
内的 yield return
确实是正确的解决方案?
I'm having a hard time finding the right LINQ syntax to use for the following iterator block:
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))
{
}
}
}
Maybe what I have is as good as it gets? For this particular code, yield return
inside an explicit foreach
is indeed the right solution?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为你的实施很好。但是,如果您想使用 LINQ(并使其稍微短一点,但不是显着缩短),那么您可以使用迭代所有操作并返回当前项和所有操作的查询来实现 GetItems其他递归生成的项目:
对于每个操作,我们生成一个序列,其中包含当前操作的项目,后跟所有递归生成的项目。通过使用嵌套的
from
子句,您可以迭代此集合以获得“平面”结构。我认为您可以通过使用支持“将元素附加到前面”操作的功能性(不可变)列表来使其变得更好 - 这正是我们在嵌套
from
中所做的。使用我的函数式编程书中的FuncList
(这不再是懒惰的)但序列):正如 Jon 提到的,没有使用查询编写递归方面的好方法(您可以使用 lambda 函数而不是方法来编写递归查询 - 但这也好不了多少)。
I think that your implementation is good. However, if you want to use LINQ (and make it a little bit - but not significantly - shorter), then you can implement
GetItems
using a query that iterates over all operations and returns current item followed by all other recursively generated items:For each operation, we generate a sequence containing the item for the current one followed by all recursively generated items. By using a nested
from
clause, you can iterate over this collection to get a "flat" structure.I think you could make it a bit nicer by using a functional (immutable) list which supports operation for "appending an element to the front" - which is exactly what we're doing in the nested
from
. UsingFuncList
from my functional programming book (this is no longer lazy sequence though):As Jon mentioned, there is no good way of writing the recursive aspect using query (you can write recursive query using lambda functions instead of methods - but that's not much better).
LINQ 通常不擅长使用标准查询运算符进行递归。您可以编写上述内容的更通用的形式,但您找不到执行此遍历的简洁标准 LINQ 方式。
LINQ isn't generally good at recursion, using the standard query operators. You could write a more general purpose form of the above, but you won't find a neat standard LINQ way of doing this traversal.
非常好。我们可以让它稍微好一点。
这是一个合理的解决方案。它很容易阅读并且清晰正确。正如我之前提到的,缺点是如果树非常深,性能可能会不好。
我将这样做:
请注意,仅当您关心迭代“按顺序”进行时,才需要调用 Reverse() 。例如,假设操作 Alpha 具有子操作 Beta、Gamma 和 Delta,并且 Delta 具有子操作 Zeta 和 Omega。遍历是这样的:
现在堆栈是空的,所以我们完成了,并且我们以“预序遍历”顺序获取项目。如果你不关心顺序,如果你需要的只是确保获得所有这些,那么就不必费心反转子级,你会按照 Alpha、Delta、Omega、Zeta 的顺序获得它们,伽玛,贝塔。
有道理吗?
It's pretty good. We can make it slightly better.
It's a reasonable solution. It's easy to read and clearly correct. The down side is, as I mentioned earlier, that the performance is potentially not good if the tree is extremely deep.
Here's how I would do this:
Note that the call to Reverse() is necessary only if you care that the iteration go "in order". For example, suppose operation Alpha has child operations Beta, Gamma and Delta, and Delta has children Zeta and Omega. The traversal goes like this:
and now the stack is empty so we're done, and we get the items in "preorder traversal" order. If you don't care about the order, if all you need is to make sure you get all of them, then don't bother to Reverse the children, and you'll get them in the order Alpha, Delta, Omega, Zeta, Gamma, Beta.
Make sense?