如何使用Stacks处理递归函数中的无限for循环?

问题描述

因此,我正在创建一个BaseIFrame类,以从任何页面通用解析嵌套的iFrame,而无需利用ID,xpath或任何特定于DOM的类。我使用了递归功能来捕获页面上的所有iFrame,并通过堆栈进行收集。但是,在调试之后,我发现递归函数在遇到堆栈溢出之前一遍又一遍地遍历同一Web元素。

我该如何纠正?

BaseIFrame 类:

namespace [Confidental namespace] {
 public class BaseIFrame {
         protected IWebDriver _driver;

         private ReadOnlyCollection<IWebElement> _iframes;

 public BaseIFrame(IWebDriver _driver) { 
        this._driver = _driver;     
         
        _iframes = _driver.FindElements(By.TagName("frame"));             
        _iframesSize = _iframes.Count();             
     }

 public ReadOnlyCollection<IWebElement> getFrames() {
            return _iframes;
        }         

private static IEnumerable GetAllFramesRecursive<T>(IWebElement frame,IWebDriver driver)
        {
            var result = new Stack<IWebElement>();
            BaseIFrame baseClass = new BaseIFrame(driver);

            var iFrameList = baseClass._iframes;
            foreach (var i in iFrameList)
            {
                result.Push(i);

                foreach (IWebElement e in GetAllFramesRecursive<IWebElement>(i,driver))
                {
                    result.Push(e);
                }
            }

            return result;
        }

        // Public callable method for unit test
        public void GetAlliFramesRecursivePublic<T>(IWebElement test,IWebDriver driver)
        {
            GetAllFramesRecursive<IWebElement>(test,driver);
        }
    }
}

BaseIFrameTest类:

        [TestMethod]
        public void AssertiFrameLength()
        {
            Login();

            var expected = 0;

            BaseIFrame bif = new BaseIFrame(_driver);
            bif.GetAlliFramesRecursivePublic<IWebElement>(bif.getFrames()[0],_driver);

            var elementSize = _driver.FindElements(By.TagName("*")).Count;

            for (var i = 0; i < elementSize; i++)
            {
                var iframesSize = _driver.FindElements(By.TagName("frame")).Count;
                expected += iframesSize;
            }

            var actual = bif.getFrames();

            Assert.AreEqual(expected,actual.Count);
        }

解决方法

一棵树的基本的深度优先迭代器看起来像这样:

        var stack = new Stack<T>();
        stack.Push(rootItem);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            // Process the current item
            foreach (var child in current.ChildItems)
            {
                stack.Push(child);
            }
        }

如果存在节点多次出现的风险(即,它是图而不是树),则需要某种方法来避免重复访问,例如使用hashSet:

        var stack = new Stack<T>();
        stack.Push(rootItem);
        var visited = new HashSet<T>();
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            if (visited.Add(current))
            {
                // Process the current item
                foreach (var child in current.ChildItems)
                {
                    stack.Push(child);
                }
            }
        }

使用迭代而不是递归既避免了stackOverflows的问题,又避免了为树中的每个节点创建新集合的需要。在我的示例中,您可以简单地执行yield return current作为生成延迟评估的迭代器的过程步骤。将current.ChildItems替换为生成当前节点的边/子项所需的任何内容。

在您的示例中看起来很奇怪的一件事是,我看不到使用frame参数的位置。似乎只有driver用于生成iFrame列表,但是驱动程序在每次调用中似乎都是相同的。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...