我有以下方法,该方法应在调用每个节点上的操作时遍历一棵树,并且每次其上移或下移层次结构时都要遍历一次。逻辑本身可以完美地工作,但是我意识到我的单元测试失败了,因为检查已经调用的元素的if语句无法完成逻辑上的意图。
public static void IterateWithHierarchyFeedback(TBase start,
Func<TBase, IEnumerable<TBase>> childSelector,
Action<TBase> invoker,
Action stepUpInvocator,
Action stepDownInvocator,
bool invokeOnStart)
{
Stack<DynamicIterator> iterationStack = new Stack<DynamicIterator>();
iterationStack.Push(new DynamicIterator(start, childSelector(start).GetEnumerator()));
while (iterationStack.Count > 0)
{
var current = iterationStack.Pop();
// HERE it fails because current.Invoked = true but invoker() is still executed
if (!current.Invoked && invokeOnStart || current.Current != start)
invoker(current.Current);
if (current.Enumerator == null || !current.Enumerator.MoveNext())
{
if (current.Current != start)
stepUpInvocator();
continue;
}
stepDownInvocator();
current.Invoked = true;
iterationStack.Push(current);
iterationStack.Push(new DynamicIterator(current.Enumerator.Current,
childSelector(current.Enumerator.Current)?.GetEnumerator()));
continue;
}
}
这是我的单元测试:
[测试] 公共异步任务TestIterateWithFeedback() { StringBuilder b =新的StringBuilder();
DynamicIterate<Tree>.Downwards.IterateWithHierarchyFeedback(_tree, t => t.Children,
tree => b.Append(tree.ReturnValue.ToString()),
() => b.Append('<'),
() => b.Append('>'),
true);
Assert.Warn(b.ToString());
const string expected = "1>2>3>4<>5<<>6<<>7>>";
Assert.AreEqual(expected, b.ToString());
}
在这里您看到输出不是应该的。这是因为invoker()
是在已调用的元素上调用的,否则按顺序输出将是正确的:
2) Expected string length 20 but was 23. Strings differ at index 8.
Expected: "1>2>3>4<>5<<>6<<>7>>"
But was: "1>2>3>4<3>5<3<2>6<2<>7<"
有人可以向我解释为什么会这样吗?我找到了this,但即使没有调试器,它也会发生。我还尝试将状态对象(DynamicIterator
)从结构更改为类(因为我最初认为这可能是异步变化的问题。但这也没有改变。
答案 0 :(得分:0)
我不确定问题出在哪里,但是迭代代码看起来比所需的更为复杂。我会提出这样的建议:
public static IEnumerable<(T Node, int Level)> DepthFirstWithlevel<T>(
T self,
Func<T, IEnumerable<T>> selector)
{
var stack = new Stack<(T Node, int Level)>();
stack.Push((self, 0));
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
foreach (var child in selector(current.Node))
{
stack.Push((child, current.Level + 1));
}
}
}
这将返回树中的每个节点以及该节点具有的树中的级别,其中0为根。如果您特别需要为每个级别更改调用方法,则可以使用单独的方法进行更改,如下所示:
public static void IterateWithHierarchyFeedback<T>(
T self,
Func<T, IEnumerable<T>> selector,
Action<T> invoker,
Action stepUp,
Action stepDown)
{
int currentLevel = 0;
foreach (var (node, level) in DepthFirstWithLevel(self, selector))
{
while (currentLevel < level)
{
currentLevel++;
stepDown();
}
while (currentLevel > level)
{
currentLevel--;
stepUp();
}
invoker(node);
}
}
对于树
A - B - C
| ⌞ D
⌞E - F
它将打印A>E>F<B>DC
,即它将首先遍历底部分支(在.Reverse()
之后插入selector(current.Node)
进行更改)。它将直接从F转到B,而无需重新访问A。