我有以下方法可以正常工作,但yield break语句只会突破当前的枚举器。我理解为什么会出现这种情况,但我在如何通过递归堆栈传播收益率分析方面留下了空白。
private static IEnumerable<Node> FindChildrenById(IEnumerable nodes, string parentText) {
var en = nodes.GetEnumerator();
var targetFound = false;
while (en.MoveNext()) {
var node = en.Current as Node;
if (node != null)
{
if (node.Parent == null && string.IsNullOrEmpty(parentText))
{
//Returns the top level nodes if an empty parentIdis entered
targetFound = true;
yield return node;
}
else if (node.Parent != null && node.Parent.Text == parentText)
{
//returns the nodes belonging to the parent
yield return node;
}
else
{
//Recurse into the children to see whether one of these is the node to find
foreach (var nd in FindChildrenById(node.Nodes, parentText))
{
yield return nd;
}
}
}
}
if (targetFound)
{
yield break;
}
}
所以当我有以下节点并将“Top 2 a”作为parentText ...
传递时Top 1
Top 1 a
Top 1 b
Top 2
Top 2 a
Top 2 aa
Top 2 ab
Top 2 ac
Top 2 b
Top 3
Top 3 a
Top 3 b
Top 4
...然后我得到了结果:
Top 2 aa
Top 2 ab
Top 2 ac
这是正确的结果,但是,当我单步执行代码时,最外面的循环继续处理前3和前4。如何突破这个外循环?
答案 0 :(得分:2)
如果我的代码正确,我想下面的代码将解决您的问题
private static IEnumerable<Node> FindChildrenById(IEnumerable nodes, string parentText)
{
var result =
(from node in nodes
where (node.Parent == null && string.IsNullOrEmpty(parentText))
|| (node.Parent != null && node.Parent.Text == parentText)
select node).TakeWhile(node => !(node.Parent == null && string.IsNullOrEmpty(parentText)));
return result;
}
它基于两种扩展方法(见下文),只应迭代直到达到目标找到的标准
public static class IEnumerablExtensions
{
//Will iterate the graph in depth first order
public static IEnumerable<TResult> Select<TResult>(this IEnumerable collection, Func<Node, TResult> selector)
{
foreach (var obj in collection)
{
var node = obj as Node;
if (node != null)
{
yield return selector(node);
foreach (var n in node.Nodes.Select(selector))
{
yield return n;
}
}
}
}
public static IEnumerable<Node> Where(this IEnumerable collection, Predicate<Node> pred)
{
foreach (var node in collection.Select(x => x)) //iterate the list in graph first order
{
if (pred(node))
yield return node;
}
}
}
编辑:原始发布中的Select方法出现错误(它没有迭代孩子的孩子)现在已经更正了
答案 1 :(得分:1)
//Returns the top level nodes if an empty parentIdis entered
targetFound = true;
yield return node;
yield break;
这对你有用吗?
<强>更新强>
我已经多考虑了一下。这可能是递归的棘手问题。你需要保留一些状态变量来打破所有循环。
如果C#有尾递归,我建议将代码转换为CPS。
你总是可以用MSIL编写它:)
答案 2 :(得分:1)
我假设该函数实际上名为FindChildrenById
,否则我看不到任何递归。
根据这个假设,你可以使用例外(我强烈建议反对),或者返回KeyValuePair<bool, IEnumerable<Node>>
,其中bool
部分将用于表示早期的链。
然后,在API级别,公开一个包装器方法,该方法只返回IEnumerable<Node>
部分并抛出bool
部分。
这是一个例子,给出了类Node
:
public class Node
{
List<Node> children;
public string Text { get; set; }
public List<Node> Children { get { return children ?? (children = new List<Node>()); } }
}
你可以像这样遍历和快捷:
public class NodeTraverser
{
private static KeyValuePair<bool, IEnumerable<Node>> GetChildrenById(string text, Node node)
{
if(node.Text == text)
{
return new KeyValuePair<bool,IEnumerable<Node>>(true, node.Children);
}
foreach(var child in node.Children)
{
var result = GetChildrenById(text, child);
if(result.Key)
{
return result; // early out
}
}
return new KeyValuePair<bool,IEnumerable<Node>>(false, Enumerable.Empty<Node>());
}
public static IEnumerable<Node> FindChildrenbyId(string text, Node root)
{
return GetChildrenById(text, root).Value;
}
}