检测无限递归?

Posted

技术标签:

【中文标题】检测无限递归?【英文标题】:Detect infinite recursion? 【发布时间】:2012-03-26 01:44:51 【问题描述】:

假设我有一个爬过数组的函数...

flatten([a, b, c, d, [e, f, g, [h, i, j, k], l], m, n, o, p])
>> [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]

Flatten 会爬过代码,遇到的每个数组都会递归地进入该数组并返回值,这样您就有了一个平面数组。

直到我们有一个数组,例如:

a    = [];
a[0] = a;

这显然会产生无限递归:

Array[1]
0: Array[1]
 0: Array[1]
  0: Array[1]
   0: Array[1]
    0: Array[1]
     0: Array[1]
      ...

如何在不修改数组的情况下检测到这种行为,以便函数可以处理这种情况?

【问题讨论】:

我们是否可以维护我们已经爬取的内容,如果我们已经爬取了那个数组,那么我们就处于上述情况...... 查看this post。它基本上展示了如何编写一个限制递归深度的函数装饰器。 【参考方案1】:

如果需要检测递归,您将不得不为此交换内存空间:创建一个您解析的对象数组(您递归发送的参数)并对照它检查每个新参数。如果你已经解析过了,立即返回。

【讨论】:

+1。另外,如果可以的话,对数组进行排序(我不熟悉 javascript 和它的对象表示,但我想你可以获取东西的地址),所以你可以使用二进制搜索,或者其他一些适合的数据结构比数组更好。 我的第一个想法是说你不能得到这样的对象的数字 id,但我实际上很确定你可以;很好的建议! 你可以使用一个集合,一个对象,它的键代表集合的元素,这些键的值可以是null(或其他一些虚拟对象)【参考方案2】:

您必须在flatten() 函数中保留一个访问过的数组数组,并在您诅咒它之前检查它们是否存在。不过,您必须将访问过的元素列表作为第二个参数传递给 recurse

function recurse(ar, visited) 
    visited = visited || [];

    function isVisited(obj) 
        for (var i=0;i<visited.length;i++) 
            if (visited[i] == obj) 
                return true;
            
        

        visted.push(obj); // but we've visited it now
        return false;
    

    // blah blah blah, do your thing... check isVisted[i]
 

检查你的数组是否很深会变得很昂贵,所以你可以作弊并在你访问的每个数组上设置一个属性,然后检查它(当然,你正在修改数组(但不是很大) )。

function recurse(ar)   
    function isVisited(obj) 
        var key = 'visited';
        var visited = obj.hasOwnProperty(key);

        obj[key] = true; // but we've visited it now

        return visited;
    

    // blah blah blah, do your thing... check isVisted[i]
 

【讨论】:

一旦弱映射被标准化/广泛可用,它们将是为每个数组添加属性的好选择。见wiki.ecmascript.org/… 因为数组也是对象,您可以为访问的每个数组设置一个属性,例如 array.visited = true;【参考方案3】:

跟踪已经访问过的数组的一种便捷方法是为每个数组添加一个“平面”属性,也许为每个操作设置一个唯一值。当每个数组都已经是一个完美的对象时,没有必要再弄乱单独的地图了。

【讨论】:

如果您拥有该对象,则为真(即您可以随意更改它),但情况可能并非总是如此。另外,您必须遍历它两次才能将 flat 属性重置为 false(然后您必须再次处理递归),或者您无法将对象展平两次。 是的 - 我猜你以后总是可以擦掉标记。【参考方案4】:

另一种更肮脏的方式(但有利于尽量减少大惊小怪)是只使用 JSON 编码器:

var is_recursive = false;

try 
    JSON.stringify(my_recursive_object);
 catch (e) 
    if (e.name = 'TypeError')
        is_recursive = true;
    else
        throw e;

我确实发现这个问题正在寻找一个更好的答案,尽管这可能会帮助那些想要一个好的 hack 的人 ;-)

【讨论】:

【参考方案5】:

在现实世界中,第一步是找到给你一个自引用对象并用棍子打他们的违规开发人员。

然而,通过将它们作为自引用消除,问题得到了解决。把它们变成副本。 Array.slice 返回数组的一部分(包括完整的副本)而不改变原始数组。

if(thisElement.constructor.prototype.slice) //is it an array
    thisElement = thisElement.slice(0); //now it's a new but identical array

请注意,这仅在主数组引用本身上。内部数组引用仍然需要更改,因为复制的引用仍然指向同一事物。

此外,如果您可以对绝对不会出现的字符做出假设,您可能会发现 slice/join 非常有用。

【讨论】:

以上是关于检测无限递归?的主要内容,如果未能解决你的问题,请参考以下文章

php递归无限极分类怎么弄

PHP面试题精讲—无限级分类/无限分类的递归算法和非递归算法-带源码建议收藏

PHP面试题精讲—无限级分类/无限分类的递归算法和非递归算法-带源码建议收藏

php实现无限级分类(递归方法)

为啥无限递归会导致段错误

无限递归函数->堆栈溢出错误