【问题标题】:Trying to return up a recursive combinations function without getting 'undefined'试图返回一个递归组合函数而不得到“未定义”
【发布时间】:2014-06-13 17:11:18
【问题描述】:

当我用 [1,2,3,4] 调用它时,它返回未定义,我不明白为什么。目标是如果数组中的任何数字组合加起来达到数组中的最大数字,则返回 true,如果不可能,则返回 false。

function ArrayAdditionI(arr) { 

  var max = Math.max.apply(null, arr);
  arr.splice(arr.indexOf(max), 1);
  var sum = function(arr) { return arr.reduce(function(a,b) { return a + b; }); };

  function combos(arr) {

    var f = function(prefix, arr) {
      for (var i = 0; i < arr.length; i++) {
        var clone = prefix.slice(0);
        clone.push(arr[i]);
        if (sum(clone) == max) { return true; }
        return f(clone, arr.slice(i+1));
      }
    }
    return f([], arr);
  }

  return combos(arr); 

}

【问题讨论】:

    标签: javascript for-loop recursion combinations


    【解决方案1】:

    f 在使用空的arr 调用时返回undefined!如果循环中没有从函数返回的测试,您将需要显式地return false。并且您不能在第一次循环时return false,而只有在找到true 时才中断并在elsewhile 时继续循环。

    要解决此问题,您需要类似

    function combos(arr) {
      function f(prefix, arr) {
        for (var i = 0; i < arr.length; i++) {
          var clone = prefix.slice(0);
          clone.push(arr[i]);
          if (sum(clone) == max) return true;
          if (f(clone, arr.slice(i+1))) return true;
        }
        return false;
      }
      return f([], arr);
    }
    

    但是,您的循环递归方案看起来也有点复杂。我宁愿使用“二叉树”的幼稚枚举,其中每个级别的节点决定当前项目是否将包含在待测试子集中:

    function ArrayAdditionI(arr) { 
      var max = Math.max.apply(null, arr);
      arr.splice(arr.indexOf(max), 1);
      var sum = function(arr) { return arr.reduce(function(a,b) { return a + b; }, 0); };
    
      function f(subset, arr) {
         return arr.length 
              ? f(subset, arr.slice(1)) || f(subset.concat([arr[0]]), arr.slice(1))
              : sum(subset) == max
      }
      return f([], arr); 
    }
    

    【讨论】:

    • 嘿,Bergi,这看起来很棒,但是当我尝试第二个(在 reduce 函数上添加起始值之后)时,我得到了超出最大调用堆栈大小。我不明白为什么第一块代码或原始设计是“有缺陷的”。感谢您的精彩解释
    • 糟糕,arr 为空时必须停止递归。现已修复。
    • 好的,您的递归尝试实际上会奏效。我对循环感到非常困惑,最初认为它会多次列举一些“前缀”(实际上是子集)甚至跳过它们。然而,在重新考虑之后,它甚至可能比简单地枚举二叉树或所有可能的子集的简单解决方案受益并且具有更少的递归调用。
    • 这是一个漂亮的功能:')! (顺便说一句,我认为array.length 应该是arr.length?)
    【解决方案2】:

    您似乎没有测试所有可能的组合。

    在这里您将测试 1+2+3、2+3、3,但绝不会测试 1+3。

    你真的想在这里有一个递归函数吗? 可能有一些更简单的方法可以找到它。

    function ArrayAdditionI(arr) { 
        var max = Math.max.apply(null, arr);
        arr.splice(arr.indexOf(max), 1);
    
        var res = arr.filter(function(num, idx) {
            var combination = false;
            // Check all combination non previously tested
            for (var i = idx; i < arr.length - 1; i++) {
                if (num + arr[i+1] === max) {
                    combination = true;
                    break;
                }
            }
            return combination;
        });
        return res.length > 0;
    }
    

    【讨论】:

    • 但是您的代码仅测试数组中 2 项的组合,而不是所有可能的组合?
    【解决方案3】:

    问题是你的f 函数没有命中 if (sum(clone) == max) { return true; } 代码行,所以它只会递归调用直到 arr.length == 0 并且它将返回未定义。

    您的变量 torf 和 results 未使用,也许您忘了对它们做些什么?

    【讨论】:

    • 不,他们只是剩下来的。那么解决方法是什么?我尝试打破 for 循环,但这也不起作用。我猜如果到 arr.length == 0 的第一组调用返回未定义,那么 for 应该继续使用 [1] 的数组上的 i = 1,但我不知道如何做到这一点
    猜你喜欢
    • 2012-09-26
    • 1970-01-01
    相关资源
    最近更新 更多