在 LINQ 中表达递归 [英] Expressing recursion in LINQ

查看:26
本文介绍了在 LINQ 中表达递归的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将 LINQ 提供程序写入分层数据源.我发现通过编写示例展示我想如何使用它来设计我的 API 最简单,然后编码以支持这些用例.

I am writing a LINQ provider to a hierarchal data source. I find it easiest to design my API by writing examples showing how I want to use it, and then coding to support those use cases.

我遇到的一件事是在 LINQ 语句中用一种简单/可重用/优雅的方式来表达深度查询"或递归.换句话说,最好的区分方法是:

One thing I am having trouble with is an easy/reusable/elegant way to express "deep query" or recursion in a LINQ statement. In other words, what is the best way to distinguish between:

from item in immediate-descendants-of-current-node where ... select item

对比:

from item in all-descendants-of-current-node where ... select item

(请注意上面的例子都不一定反映我想要的查询的结构.我对任何表达递归/深度的好方法感兴趣)

( please note neither of those examples above necessarily reflect the structure of the query I want. I am interested in any good way to express recursion/depth)

请注意我不是在问如何实现这样的提供者,或者如何以允许递归的方式编写我的 IQueryable 或 IEnumerable.我是从编写 LINQ 查询并使用我的提供程序的人的角度询问的 - 他们表达是否要递归的直观方式是什么?

Please note I am not asking how to implement such a provider, or how to write my IQueryable or IEnumerable in such a way that allows recursion. I am asking from the standpoint of a person writing the LINQ query and utilizing my provider - what is an intuitive way for them to express whether they want to recurse or not?

数据结构类似于典型的文件系统:文件夹可以包含子文件夹的集合,文件夹也可以包含项目的集合.因此,myFolder.Folders 表示作为 myFolder 的直接子级的所有文件夹,而 myFolder.Items 包含直接在 myFolder 中的所有项目.这是站点层次结构的基本示例,很像包含文件夹和页面的文件系统:

The data structure resembles a typical file system: a folder can contain a collection of subfolders, and a folder can also contain a collection of items. So myFolder.Folders represents all the folders who are immediate children of myFolder, and myFolder.Items contains all the items immediately within myFolder. Here's a basic example of a site hierachy, much like a filesystem with folders and pages:

(F)Products
    (F)Light Trucks
        (F)Z150
            (I)Pictures
            (I)Specs
            (I)Reviews
        (F)Z250
            (I)Pictures
            (I)Specs
            (I)Reviews
        (F)Z350
            (I)Pictures
            (I)Specs
            (I)Reviews
        (I)Splash Page
    (F)Heavy Trucks
    (F)Consumer Vehicles
    (I)Overview 

如果我写:

from item in lightTrucks.Items where item.Title == "Pictures" select item

表达查询获取轻型卡车下的所有项目或仅直接项目的意图的最直观方式是什么?区分两种意图的干扰最少、摩擦最少的方法?

What is the most intuitive way to express an intent that the query get all items underneath Light Trucks, or only the immediate ones? The least-intrusive, lowest-friction way to distinguish between the two intents?

我的 #1 目标是能够将这个 LINQ 提供程序转交给对 LINQ 有一般了解的其他开发人员,并允许他们编写递归和列表查询,而无需向他们提供有关编写递归 lambda 的教程.给定一个看起来不错的用法,我可以针对它对提供程序进行编码.

My #1 goal is to be able to turn this LINQ provider over to other developers who have an average understanding of LINQ and allow them to write both recursive and list queries without giving them a tutorial on writing recursive lambdas. Given a usage that looks good, I can code the provider against that.

额外说明:(我真的很不擅长传达这一点!) - 这个 LINQ 提供者是一个外部系统,它不仅仅是遍历对象图,在这种特定情况下也没有递归表达式 实际上可以转化为任何一种真正的递归活动.只需要一种方法来区分深"查询和浅"查询.

Additional clarification: (I am really sucking at communicating this!) - This LINQ provider is to an external system, it is not simply walking an object graph, nor in this specific case does a recursive expression actually translate into any kind of true recursive activity under the hood. Just need a way to distinguish between a "deep" query and a "shallow" one.

那么,你认为最好的表达方式是什么?或者有没有我错过的标准表达方式?

So, what do you think is the best way to express it? Or is there a standard way of expressing it that I've missed out on?

推荐答案

Linq-toXml 可以很好地处理这个问题,有一个 XElement.Elements()/.Nodes() 操作来获取直接子元素,还有一个 XElement.Descendents()/DescendentNodes() 操作以获取所有后代.你会认为这是一个例子吗?

Linq-toXml handles this fine, there is an XElement.Elements()/.Nodes() operation to get immediate children, and a XElement.Descendents()/DescendentNodes() operations to get all descendents. Would you consider that as an example?

总结 Linq-to-Xml 的行为...每个导航功能对应于 XPath 中的轴类型 (http://www.w3schools.com/xpath/xpath_axes.asp).如果导航功能选择元素,则使用轴名称.如果导航功能选择节点,则使用轴名称并附加节点.

To summarize Linq-to-Xml's behavior... The navigation functions each correspond to an axis type in XPath (http://www.w3schools.com/xpath/xpath_axes.asp). If the navigation function selects Elements, the axis name is used. If the navigation function selects Nodes, the axis name is used with Node appended.

例如,有函数 Descendants() 和 DescendantsNode() 对应于 XPath 的后代轴,返回 XElement 或 XNode.

For instance, there are functions Descendants() and DescendantsNode() correspond to XPath's descendants axis, returning either an XElement or an XNode.

例外情况并不奇怪,它是最常用的情况,即 children 轴.在 XPath 中,如果未指定轴,则这是使用的轴.为此,linq-to-xml 导航功能不是 Children() 和 ChildrenNodes(),而是 Elements() 和 Nodes().

The exception case is not surprisingly the most used case, the children axis. In XPath, this is the axis used if no axis is specified. For this, the linq-to-xml navigation functions are not Children() and ChildrenNodes() but rather Elements() and Nodes().

XElement 是 XNode 的子类型.XNode 包括 HTML 标签之类的内容,但也包括 HTML 注释、cdata 或文本.XElements 是一种 XNode,但特指 HTML 标签.XElements 因此有一个标签名称,并支持导航功能.

XElement is a subtype of XNode. XNode's include things like HTML tags, but also HTML comments, cdata or text. XElements are a type of XNode, but refer specifically to HTML tags. XElements therefore have a tag name, and support the navigation functions.

现在在 Linq-to-XML 中链接导航不像 XPath 那样容易.问题是导航函数返回集合对象,而导航函数应用于非集合.考虑 XPath 表达式,它选择一个表标签作为直接子标签,然后选择任何后代表数据标签.我认为这看起来像./children::table/descendants::td"或./table/descendants::td"

Now its not as easy to chain navigations in Linq-to-XML as it is XPath. The problem is that navigation functions return collection objects, while the navigation functions are applied to non-collections. Consider the XPath expression which selects a table tag as an immediate child then any descendant table data tag. I think this would look like "./children::table/descendants::td" or "./table/descendants::td"

使用 IEnumerable<>::SelectMany() 允许在集合上调用导航函数.相当于上面的看起来像 .Elements("table").SelectMany(T => T.Descendants("td"))

Using IEnumerable<>::SelectMany() allows one to call the navigation functions on a collection. The equivalent to the above looks something like .Elements("table").SelectMany(T => T.Descendants("td"))

这篇关于在 LINQ 中表达递归的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆