当前位置: 首页 > 编程语言 > C#

在LINQ中表达递归Share

时间:2023-04-10 13:39:00 C#

在LINQ中表达递归我发现通过编写示例来展示我想如何使用它,然后编写代码来支持这些用例来设计我的API是最简单的。我遇到麻烦的一件事是在LINQ语句中表达“深度查询”或递归的一种简单/可重用/优雅的方式。换句话说,最好的区分方式是:从当前节点的直接后代中的项目...选择项目与:从当前节点的所有后代中的项目...选择项目(编辑:请注意,上面的示例都不一定反映我想要的查询结构。我对表达递归/深度的任何好方法感兴趣)注意我不是在问如何实现这样的提供者,或者如何允许递归地编写我的IQueryable或IEnumerable。我是从编写LINQ查询并利用我的提供程序的人的角度来问的——他们表达是否需要递归的直观方式是什么?数据结构类似于典型的文件系统:文件夹可以包含子文件夹的集合,文件夹可以包含项目的集合。所以myFolder.Folders表示所有文件夹都是myFolder的直接子文件夹,而myFolder.Items包含myFolder中的所有项目。这是网站层次结构的基本示例,很像带有文件夹和页面的文件系统:(F)产品(F)LightTrucks(F)Z150(I)图片(I)规格(I)评论(F)Z250(I))图片(I)规格(I)评论(F)Z350(I)图片(I)规格(I)评论(I)启动页面(F)重型卡车(F)消费车辆(I)概述如果我写:从iteminlightTrucks.Itemswhereitem.Title=="Pictures"selectitem表达查询意图以获取轻型卡车下方的所有项目或仅获取直接项目的最直观方式是什么?区分这两种意图的侵入性最小、摩擦最小的方法?我的第一个目标是能够将此LINQ提供程序转换为对LINQ有一般了解的其他开发人员,并允许他们编写递归和列表查询,而无需为他们提供有关编写递归lambda的教程。鉴于用法看起来不错,我可以针对它编写代码。另外澄清:(我真的很想传达这个!)-这个LINQ提供程序是一个外部系统,它不是简单地遍历对象图,也不是在这种特定情况下,递归表达式实际上被转换为任何类型真正的递归活动是在引擎盖下。只需要一种方法来区分“深”查询和“浅”查询。那么您认为最好的表达方式是什么?还是有一种标准的方式来表达我所缺少的?Linq-toXml处理得很好,有一个XElement.Elements()/。Nodes()操作获取直接子节点,还有一个XElement.Descendents()/DescendentNodes()操作获取所有后代。你会认为这是一个例子吗?总结Linq-to-Xml的行为...每个导航函数对应于XPath(http://www.w3schools.com/xpath/xpath_axes.asp)中的一个轴类型。如果为导航功能选择“元素”,则使用轴名称。如果导航功能选择了一个节点,则轴名称将与附加节点一起使用。例如,函数Descendants()和DescendantsNode()对应于XPath的后代轴,返回XElement或XNode。例外并不奇怪,最常见的情况是子轴。在XPath中,如果未指定轴,则使用此轴。为此,linq-to-xml的导航函数不是Children()和ChildrenNodes(),而是Elements()和Nodes()。XElement是XNode的子类型。XNode包括HTML标签,也包括HTML注释、cdata或文本。XElements是XNode的一种,但特指HTML标签。因此,XElements具有标签名称并支持导航功能。现在,Linq-to-XML中的链接导航不像XPath那样容易。问题在于导航函数返回集合对象,而导航函数应用于非集合。考虑一个XPath表达式,它选择一个表标记作为直接子标记,然后选择任何后代标记作为数据标记。我认为这看起来像“./children::table/descendants::td”或“./table/descendants::td”使用IEnumerable::SelectMany()可以调用集合上的导航功能。上面的等效项类似于.Elements("table")。SelectMany(T=>T.Descendants("td"))嗯,首先要注意的是,其实lambda表达式是可以递归的。不,老实说!这不容易做到,当然也不容易阅读-大多数LINQ提供程序(除了LINQ-to-Objects,它更简单)只会看着它并咳嗽......但这是可能的。在此处查看完整的血腥细节(警告-这可能是脑痛)。然而!!这可能无济于事......对于一种实用的方法,我会看看XElement等的方式......注意你可以使用Queue或Stack来删除一些递归:usingSystem;使用System.Collections.Generic;staticclassProgram{staticvoidMain(){Nodea=newNode("a"),b=newNode("b"){Children={a}},c=newNode("c"){Children={b}};foreach(c.Descendents()中的节点节点){Console.WriteLine(node.Name);}}}classNode{//非常简单;没有完整性检查等publicstringName{get;私有集;}publicListChildren{get;私有集;}publicNode(stringname){Name=name;孩子=新名单();}}staticclassNodeExtensions{publicstaticIEnumerableDescendents(thisNodenode){if(node==null)thrownewArgumentNullException("node");if(node.Children.Count>0){foreach(node.Children中的子节点){yieldreturnchild;foreach(Descendents(child)中的节点desc){yieldreturndesc;}}}}}另一种方法是编写类似SelectDeep的东西(模仿单级SelectMany):publicstaticclassEnumerableExtensions{publicstaticIEnumerableSelectDeep(thisIEnumerablesource,Func>selector){foreach(Titeminsource){yieldreturnitem;foreach(TsubIteminSelectDeep(selector(item),selector)){yieldreturnsubItem;}}}}publicstaticclassNodeExtensions{publicstaticIEnumerableDescendents(thisNodenode){if(node==null)thrownewArgumentNullException("node");返回node.Children.SelectDeep(n=>n.Children);同样,我没有优化它来避免递归,但它可以很容易地完成,我会以这样一种方式实现它,即我可以控制我想要查询的深度。像Descendants()这样的东西会检索所有级别的后代,而Descendants(0)会检索直接的孩子,Descendants(1)会得到孩子和孙子等......我只想实现两个功能来清楚地区分这两个选项(孩子与FullDecendants)或重载GetChildren(boolreturnDecendants)。每个都可以实现IEnumerable,因此它只是将它们传递到LINQ语句中的函数。您可能希望为您的类型实现FlattenRecusively(extension)方法。fromiteminlist.FlattenRecusively()where...selectitemRex,你当然开启了一个有趣的讨论,但你似乎已经排除了所有的可能性——也就是说,你似乎拒绝(1)让消费者编写递归逻辑,(2)让你的节点类公开大于一个度的关系。或者您可能还没有完全排除(2)。我可以想到另一种方法,它几乎与GetDescendents方法(或属性)一样具有表现力,但可能不那么“笨拙”(取决于树的形状)......从iteminAllItemswhereitem.Parent==currentNodeselectitem和iteminAllItemswhereitem.Ancestors.Contains(currentNode)选择项目我必须同意Frank。查看LINQ-to-XML如何处理这些场景。事实上,我完全模仿了LINQ-to-XML实现,但对任何数据类型进行了更改。为什么要重新发明轮子?你能举起重物吗?(它甚至没有那么重)使用系统;使用系统集合;使用System.Collections.Generic;使用System.Linq;namespaceLinqRecursion{classProgram{staticvoidMain(string[]args){Personmom=newPerson(){Name="Karen"};Personme=newPerson(mom){Name="Matt"};PersonyoungerBrother=newPerson(mom){Name="Robbie"};PersonolderBrother=newPerson(mom){Name="Kevin"};Personnephew1=newPerson(olderBrother){Name="Seth"};Personnephew2=newPerson(olderBrother){Name="Bradon"};PersonolderSister=newPerson(mom){Name="Michelle"};Console.WriteLine("tAll");//全部//Karen0//Matt1//Robbie2//Kevin3//Seth4//Bradon5//Michelle6foreach(variteminmom)Console.WriteLine(item);Console.WriteLine("rntOdds");//赔率//Matt1//Kevin3//Bradon5varodds=mom.Where(p=>p.ID%2==1);foreach(variteminodds)Console.WriteLine(item);Console.WriteLine("rntEvens");//Evens//Karen0//Robbie2//Seth4//Michelle6varevens=mom.Where(p=>p.ID%2==0);foreach(variteminevens)Console.WriteLine(item);控制台.ReadLine();}}publicclassPerson:IEnumerable{privatestaticint_idRoot;publicPerson(){_id=_idRoot++;}publicPerson(Personparent):this(){Parent=parent;parent.Children.Add(这个);}私人int_id;publicintID{get{return_id;}}公共字符串名称{得到;放;}publicPersonParent{get;私有集;私人名单_children;publicListChildren{get{if(_children==null)_children=newList();返回_children;}}publicoverridestringToString(){returnName+""+_id.ToString();}#regionIEnumerableMemberspublicIEnumeratorGetEnumerator(){yieldreturnthis;foreach(varchildinthis.Children)foreach(variteminchild)yieldreturnitem;}#endregion#regionIEnumerable成员IEnumeratorIEnumerable.GetEnumerator(){returnthis.GetEnumerat或者();}#endregion}}我只是使用扩展方法来遍历树哦等等,我已经这样做了!?以上就是C#学习教程:表达LINQ递归共享的全部内容。如果对大家有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权请点击右侧联系管理员删除。如需转载请注明出处: