使用yield的方法不允许调用自己
本文关键字:调用 自己 不允许 方法 yield 使用 | 更新日期: 2023-09-27 18:09:57
这很可能是一个用户错误(我有点希望如此)。我在c#中遇到了一个奇怪的情况,如果我试图在使用yield的方法中进行递归调用,它似乎不受尊重(即调用被忽略)。
下面的程序说明了这一点:// node in an n-ary tree
class Node
{
public string Name { get; set; }
public List<Node> ChildNodes { get; set; }
}
class Program
{
// walk tree returning all names
static IEnumerable<string> GetAllNames(IEnumerable<Node> nodes)
{
foreach (var node in nodes)
{
if (node.ChildNodes != null)
{
Console.WriteLine("[Debug] entering recursive case");
// recursive case, yield all child node names
GetAllNames(node.ChildNodes);
}
// yield current name
yield return node.Name;
}
}
static void Main(string[] args)
{
// initalize tree structure
var tree = new List<Node>
{
new Node()
{
Name = "One",
ChildNodes = new List<Node>()
{
new Node() {Name = "Two"},
new Node() {Name = "Three"},
new Node() {Name = "Four"},
}
},
new Node() {Name = "Five"}
};
// try and get all names
var names = GetAllNames(tree);
Console.WriteLine(names.Count());
// prints 2, I would expect it to print 5
}
}
您正在拨打电话,但没有对其进行任何操作。你需要在这里使用结果
static IEnumerable<string> GetAllNames(IEnumerable<Node> nodes) {
foreach (var node in nodes) {
if (node.ChildNodes != null) {
foreach (var childNode in GetAllNames(node.ChildNodes)) {
yield return childNode;
}
}
yield return node.Name;
}
}
您没有返回递归调用的结果。
您需要yield return
从调用返回的每个项目:
foreach(var x in GetAllNames(node.ChildNodes))
yield return x;
这是一个非常有趣的问题,它会导致对任意深度结构的大量资源利用。我想斯基特先生提出了一种"扁平化"技术,但我不记得其中的联系。下面是我们基于他的想法使用的代码(这是IEnumerable的一个扩展方法):
public static class IEnumerableExtensions
{
/// <summary>
/// Visit each node, then visit any child-list(s) the node maintains
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="subjects">IEnumerable to traverse/></param>
/// <param name="getChildren">Delegate to get T's direct children</param>
public static IEnumerable<T> PreOrder<T>(this IEnumerable<T> subjects, Func<T, IEnumerable<T>> getChildren)
{
if (subjects == null)
yield break;
// Would a DQueue work better here?
// A stack could work but we'd have to REVERSE the order of the subjects and children
var stillToProcess = subjects.ToList();
while (stillToProcess.Any())
{
// First, visit the node
T item = stillToProcess[0];
stillToProcess.RemoveAt(0);
yield return item;
// Queue up any children
if (null != getChildren)
{
var children = getChildren(item);
if (null != children)
stillToProcess.InsertRange(0, children);
}
}
}
}
这避免了递归和大量嵌套迭代器。然后调用:
// try and get all names
var names = tree.PreOrder<Node>(n => n.ChildNodes);
现在这是一个"预购",节点名称放在前面,但如果你喜欢的话,你可以很容易地写一个后订单。