【问题标题】:JavaScript - How to reset a local variable during recursion?JavaScript - 如何在递归期间重置局部变量?
【发布时间】:2018-05-12 10:53:31
【问题描述】:

有一个具有“值”或/和“子项”作为属性的对象。问题是将所有值相加并返回总和。这是有效的。我使用了递归。我正在使用全局变量来存储总和。

请参考-https://jsfiddle.net/ws6ty78b/1/

function sumup(node) {
  sum+=node.value;
  if (node.children && node.children.length > 0) {
    for (var i =0; i < node.children.length; i++) {
        sumup(node.children[i]);
    }
}
return sum
}

问题 - 再次调用相同的函数时,我没有得到相同的结果。总和翻了一番。我知道为什么会这样,因为我使用的是全局变量。

问题 - 对于上述问题,有没有一种方法可以让任意数量的调用得到相同的结果。

请注意限制 -

1) 不要更改函数签名和声明。

2) 在“sumup”函数中使用“sum”变量。

3) 不要使用任何额外的变量。

4) 仅在 sumup 函数内进行更改。

【问题讨论】:

  • Please note the restrictions 很做作,有点傻……
  • @CertainPerformance - 不比坐在面试官面前一无所知更傻!
  • “不要更改函数签名,包括定义和声明”,然后“只在 sumup 函数内进行更改”...有什么变化?您不允许任何更改。
  • @GerardoFurtado 感谢您的指出!修改ofcourse的定义!不是签名和声明
  • 'function sumup(xyz)' 和 'sumup(abc)' 将保持不变。基本上没有柯里化或添加额外的参数。希望清楚。

标签: javascript recursion tree closures global


【解决方案1】:

您可以简单地将每个子节点的值与当前节点的值相加:

function sumup(node) {
      sum=node.value;
      if (node.children && node.children.length > 0) {
        for (var i =0; i < node.children.length; i++) {
          sum+=sumup(node.children[i]);
        }
      }
      return sum
    }

【讨论】:

  • @Lucalachini - 在您的代码中,我无法想象的一件事是,在递归期间不会重新初始化 sum 变量吗? (我知道它工作正常!)
  • @Bijoyvalappil 之所以有效,是因为 js 解释器在计算表达式期间捕获了 sum 变量的值。如果您尝试sum=sumup(node.children[i]) + sum;,该解决方案将不再有效。
  • @Lucalachini - 哦!首先,我问第二行'sum = node.value'为什么在递归期间没有被重置。行 sum=sumup(node.children[i]) + sum;有效,我想知道为什么它不起作用!
  • @Bijoyvalappil 每次都会重置总和,但总和表达式会保留总和变量的旧值。实际上,如果您按照我的说明替换该行,您将得到错误的结果,因为函数 sumup 是在解释器计算 sum 变量之前执行的。我在您的 JSFiddle 上进行了测试,结果是 18 而不是 16。
【解决方案2】:

您可以在递归和分配/测试自定义this 时进行call 总结:

var obj = {
  "value": 4,
  "children": [{
      "value": 2,
      "children": [{
        "value": 1
      }]
    },
    {
      "value": 9
    }
  ]
};
var sum = 0;

function sumup(node) {
  //Only make change within this function body
  if (!this || !this.recurse) sum = 0;
  sum += node.value;
  if (node.children && node.children.length > 0) {
    for (var i = 0; i < node.children.length; i++) {
      sumup.call({ recurse: true }, node.children[i]);
    }
  }
  return sum
}

console.log(sumup(obj));
console.log(sumup(obj));

或者,您可以完全取消全局变量,并在子级上使用递归 reduce:

var obj = {
  "value": 4,
  "children": [{
      "value": 2,
      "children": [{
        "value": 1
      }]
    },
    {
      "value": 9
    }
  ]
};

const sumup = (node) => (
  node.value + (node.children
    ? node.children.reduce((a, child) => a + sumup(child), 0)
    : 0
  )
);

console.log(sumup(obj));
console.log(sumup(obj));

【讨论】:

  • 我认为所有递归问题都有一个基本条件。我们应该考虑将 for 循环迭代作为基本情况吗?
【解决方案3】:

为了实现您的目标,函数sumup() 将被调用 N 次。我们不知道这个 N 是多少,因为它取决于子节点的递归次数。另外,由于限制,我们不能编辑函数签名,也不能在别处编写代码来做任何事情,甚至不能手动将 sum 设置为 0。

因此,我们需要一种方法来区分一组调用和下一组调用,并重置它。

我的想法是我们设置一个阈值,使用closure。在示例中,从第一次调用开始,您有 5 秒的时间继续调用,之后它将重新开始。除非提供另一种区分调用的方法,否则我们确实可以做到这一点。

编辑:我在这里尝试的是将所有递归保留为基本情况,而不是更改对象。由于 OP 已接受该解决方案,因此我添加了一个节点重置器属性。如果任何节点的重置器未定义或为空或零或未定义,这将重置总和。我们必须假设它没有在启动对象中定义。正如我所说,这是一个弱假设。通过在递归时定义它,当前总和被结转。 我也会保留最初的时间阈值想法,以备最终兴趣。

var obj = {
  "value": 4,
  "children": [
    {
      "value": 2,
      "children": [
        {
          "value": 1
        }
      ]
    },
    {
      "value": 9
    }
  ]
}


const sumup = (function(){
  var lastTime = 0;
  var newTime = 0;
  var sum = 0;
  const timeThreshold = 5000; // 5 seconds
  return function(node) {
    newTime = new Date().getTime();
    if(!node["resetter"] || (newTime-lastTime >= timeThreshold)){
      sum=0;
      lastTime = newTime;
      }
      sum+=node.value;
      if (node.children && node.children.length > 0) {
        for (var i =0; i < node.children.length; i++) {
          sumup(Object.assign({"resetter":true},node.children[i]));
        }
      }
      return sum;
   }
})();

console.log(sumup(obj)); //16
console.log(sumup(obj)); //32! should print 16 everytime

【讨论】:

  • 那行不通 - console.log(sumup(obj)); 被串行调用,没有停机时间,并且更改函数之外的任何内容以使其不会在另一个之后立即调用它们是不允许的.
  • 在您澄清后,我使用 Object.assign 添加了替代解决方案。
猜你喜欢
  • 2015-03-08
  • 2015-04-23
  • 1970-01-01
  • 2013-07-14
  • 2017-02-17
  • 1970-01-01
  • 2018-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多