简体   繁体   English

假 if 语句仍然执行

[英]False if-statement still executes

I have the following method which should iterate through a tree while invoking actions on each node plus once every time it steps up or down the hierarchy.我有以下方法,它应该遍历树,同时在每个节点上调用操作,并且每次在层次结构上向上或向下时调用一次。 The logic itself works flawless, but I realized that my unit test fails because the if-statement which checks for already invoked elements fails to do what it is logical meant to do.逻辑本身完美无缺,但我意识到我的单元测试失败了,因为检查已调用元素的 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;
    }
}

This is my unit test:这是我的单元测试:

[Test] public async Task TestIterateWithFeedback() { StringBuilder b = new StringBuilder(); [测试] 公共异步任务 TestIterateWithFeedback() { StringBuilder b = new 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());

} }

And here you see that the output is not what it should be.在这里你看到输出不是它应该的样子。 That's because invoker() is called on elements that were already invoked, otherwise the output would be correct when it comes to order:那是因为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<"

Can anyone explain to me why this happens?谁能向我解释为什么会发生这种情况? I found this but even without debugger it occurs.我发现了这一点,但即使没有调试器,它也会发生。 I also tried to change my state object ( DynamicIterator ) from struct to class (as I initially thought this might be an issue with the async variation. But that's not changing anything either.我还尝试将我的状态对象( DynamicIterator )从 struct 更改为 class (因为我最初认为这可能是异步变体的问题。但这也没有改变任何东西。

I'm not sure that the problem is, but the iteration code looks more complicated than needed.我不确定问题是什么,但迭代代码看起来比需要的更复杂。 I would propose something like this:我会提出这样的建议:

    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));
            }
        }
    }

This would return each node in the tree, together with the level in the tree the node has, with 0 being the root.这将返回树中的每个节点,以及该节点在树中的级别,0 是根。 If you specifically need methods to be called for each level change, you can make that with a separate method, something like this:如果您特别需要为每个级别更改调用方法,您可以使用单独的方法来实现,如下所示:

    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);
        }
    }

For the tree为树

A - B - C
  |  ⌞ D
   ⌞E - F

It will print A>E>F<B>DC , ie It will traverse the bottom branches first (insert a .Reverse() after selector(current.Node) to change this).它将打印A>E>F<B>DC ,即它将首先遍历底部分支.Reverse()selector(current.Node)之后插入一个.Reverse() selector(current.Node)来改变它)。 And it will go from the F to B directly, without revisiting A.它将直接从 F 转到 B,而无需重新访问 A。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM