如何防止无限递归

发布于 2024-10-11 09:48:42 字数 995 浏览 2 评论 0原文

我正在尝试递归打印 jQuery 的内容。我计划使用它来根据已知“jQuery 签名”的小型数据库来分析 jQuery 的现有实例(在页面中加载),以确定已进行哪些更改(即已加载哪些插件、修改了哪些功能等) .)。

为此,我有一个小函数:

function recurse(obj, iter){
    var padding = (new Array(iter + 1)).join("  ") + ">";

    for (var i in obj){
    document.writeln(padding + i + "<br/>");

    if (iter < 5)
        recurse(obj[i], iter + 1);
    }
}

当我执行这个时:

recurse(jQuery, 1);

我得到这样的结果:

  >prototype
    >init
      >prototype
        >init
          >prototype
        >selector
        >jquery
          >0

.... On and on and on .....

我的问题是,在一开始,你可以看到 prototype 然后 init 一遍又一遍地重复。它停止在 5 深度的唯一原因是 if (iter < 5) 检查。如果没有限制,它就会永远重复[原文如此?]。迭代限制有帮助,但如果关键函数有 6 层深度怎么办?本质上,我不知道应该对这个迭代进行什么限制,或者是否应该有一个。

相反,我认为必须有某种算法可以防止永无止境的递归。这样的算法存在吗?或者我应该改变遍历 jQuery 的方式?谢谢,

I am trying to recursively print out the contents of jQuery. I'm planning on using this to analyze an existing instance of jQuery (loaded in a page) against a small database of known "jQuery signatures", to determine what changes have been made (i.e. what plugins have been loaded, functions modified, etc.).

To do this, I have this small function:

function recurse(obj, iter){
    var padding = (new Array(iter + 1)).join("  ") + ">";

    for (var i in obj){
    document.writeln(padding + i + "<br/>");

    if (iter < 5)
        recurse(obj[i], iter + 1);
    }
}

When I execute this:

recurse(jQuery, 1);

I get something like this:

  >prototype
    >init
      >prototype
        >init
          >prototype
        >selector
        >jquery
          >0

.... On and on and on .....

My problem is, at the very beginning, you can see that prototype and then init repeat over and over again. The only reason it stopped at 5 deep is because of the if (iter < 5) check. If the limit wasn't there, it would have recurred [sic?] forever. The iteration limit helps, but what if there is a critical function 6 deep? Essentially, I have no idea what I should make this iteration limit, or if there should be one at all.

Instead, I'm thinking there must be some kind of algorithm that can prevent never-ending recursion. Does such an algorithm exist? Or should I change how I go about traversing jQuery? Thanks,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

遥远的她 2024-10-18 09:48:42

您可以跟踪已经看到的值,并在再次看到该值时退出。

function recurse(obj) {
  var marker = '__' + new Date().getTime() + '__';
  function r(obj, iter) {
    if (marker in obj) return;

    var padding = (new Array(iter + 1)).join("  ") + ">";
    obj[marker] = true;

    for (var i in obj) {
      if (!obj.hasOwnProperty(i) || i === marker) continue;

      document.writeln(padding + i + "<br/>");

      recurse(obj[i], iter + 1);
    }
  }
  r(obj, 0);      
}

现在这当然有一个缺点,那就是让你遍历的对象图充满额外的属性,但对于某些应用程序来说这不是问题。另外,用时钟来做一个“独特”的标记确实很蹩脚;您可能只想使用一个计数器,或者只是一个固定的无意义字符串。

编辑 - 另外,另一个问题(也存在于原始代码中)是,这确实应该检查“obj”值是否真的是对象。如果它们是标量,那么真的没有必要做任何事情。您只需要在检查标记后立即进行“typeof”检查,如果您看到 null、数字、字符串或布尔值,则只需返回即可。

You could keep track of what values you've already seen, and just bail out when you see one again.

function recurse(obj) {
  var marker = '__' + new Date().getTime() + '__';
  function r(obj, iter) {
    if (marker in obj) return;

    var padding = (new Array(iter + 1)).join("  ") + ">";
    obj[marker] = true;

    for (var i in obj) {
      if (!obj.hasOwnProperty(i) || i === marker) continue;

      document.writeln(padding + i + "<br/>");

      recurse(obj[i], iter + 1);
    }
  }
  r(obj, 0);      
}

Now this of course has the disadvantage of leaving your traversed object graph littered with extra properties, but for some applications that wouldn't be a problem. Also using the clock to make a "unique" marker is really lame; you might want to just use a counter, or maybe just a fixed nonsense string.

edit — Also, another issue (present in the original code too) is that this really should be checking to see if the "obj" values are really objects. If they're scalars, then there's no point doing anything, really. You'd just need to do a "typeof" check right after checking for the marker, and if you see null, a number, a string, or a boolean, just return.

挽你眉间 2024-10-18 09:48:42

您的递归缺少基本情况。请参阅递归的定义。您引入了(深度 < 5) 的任意基本情况。也许改为使用数组的长度,或者正如 Pointy 指出的那样,使用 hasOwnProperty 检查来跳过递归调用。

Your recursion is missing a base case. See the definition of Recursion. You introduced an arbitrary base case of (depth < 5). Maybe instead use the length of the array, or as Pointy pointed out, the hasOwnProperty check to skip the recursive call.

烛影斜 2024-10-18 09:48:42

除非我遗漏了一些东西,否则您需要做的就是跳过字符串(如果您使用允许字符串索引的现代浏览器,否则没关系)和 init 函数,它是 jQuery 自引用,为您提供无限递归

function recurse(obj, iter){
    var padding = (new Array(iter + 1)).join("  ") + ">";

    for (var i in obj){
        document.writeln(padding + i + "<br/>");
        if (i != 'init' && typeof obj[i] != 'string') 
            recurse(obj[i], iter + 1);
    }
}

Unless i am missing something, all you need to do is to skip strings (if you use a modern browser that allows indexing of strings, otherwise it doesn't matter) and the init function which is the jQuery self reference that gives you the infinite recursion

function recurse(obj, iter){
    var padding = (new Array(iter + 1)).join("  ") + ">";

    for (var i in obj){
        document.writeln(padding + i + "<br/>");
        if (i != 'init' && typeof obj[i] != 'string') 
            recurse(obj[i], iter + 1);
    }
}
时光倒影 2024-10-18 09:48:42

正如几个答案所说:您的算法必须包含适当的中断情况以防止无限递归。

但是,如果您无法控制算法的输入,会发生什么情况?

对于这种情况,或者只是出于开发目的,您可以使用以下内容:

function acmeRecursion(){
  var count = localStorage.getItem("count");
  if(count>50){
    throw new Error("recursion limit exceeded");
  }
  count++;

  //put your logic here
}

As several answers said: You algorithm must contain a proper break case to prevent infinite recursion.

But what happen if you cannot control the input of your algorithm?

For that case, or just for development purposes, you could use this:

function acmeRecursion(){
  var count = localStorage.getItem("count");
  if(count>50){
    throw new Error("recursion limit exceeded");
  }
  count++;

  //put your logic here
}
被翻牌 2024-10-18 09:48:42

根据 Pointy 的答案(理想情况下这将是一条注释,但是可惜,代码在这些中效果不佳),更好的解决方案可能是将一个对象传递给递归函数,该函数将跟踪您的对象我们已经看过了。像这样的东西:

var recurse = function(obj)
{
    var seen = {};
    var inner = function(obj, padding)
    {
        for (var i in obj)
        {
            if (!(obj[i] in seen))
            {
                document.writeln(padding + i + '<br />');
                seen[obj[i]] = true;
                inner(obj[i], padding + '  ');
            }
        }
    };
    return inner(obj, '');
};

为了简单起见,它使用闭包而不是参数来传递 seen 对象,但基本概念是相同的。

这种方法的优点是不会向正在遍历的对象添加额外的属性。

编辑:我本想解释这一点,但忘了。我没有使用 hasOwnProperty这里是因为在打印对象图的情况下,您可能确实希望看到继承的属性。

Building off of Pointy's answer (ideally this would be a comment, but alas, code doesn't work great in those), a better solution might be to just pass along an object to the the recurse function that will keep track of objects you've already seen. Something like this:

var recurse = function(obj)
{
    var seen = {};
    var inner = function(obj, padding)
    {
        for (var i in obj)
        {
            if (!(obj[i] in seen))
            {
                document.writeln(padding + i + '<br />');
                seen[obj[i]] = true;
                inner(obj[i], padding + '  ');
            }
        }
    };
    return inner(obj, '');
};

Which uses a closure rather than an argument to pass along the seen object, for the sake of simplicity, but the basic concept is the same.

This approach has the advantage of not adding extra attributes to the objects you're traversing.

Edit: I meant to explain this, but forgot. I'm not using hasOwnProperty here because in the case of printing an object graph, you probably do want to see inherited attributes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文