有可能降低这个Javascript算法解决方案的复杂性和意大利面条质量吗?

时间:2016-06-15 12:47:47

标签: javascript complexity-theory

问题: 创建一个将两个参数相加的函数。 如果只提供了一个参数,则返回一个需要一个参数并返回总和的函数。

例如,addTogether(2,3)应返回5,addTogether(2)应返回一个函数。

使用单个参数调用此返回的函数将返回总和: var sumTwoAnd = addTogether(2); sumTwoAnd(3)返回5.

如果任一参数不是有效数字,则返回undefined。

解决方案应该返回:

addTogether(2,3)应该返回5。 addTogether(2)(3)应该返回5。 addTogether(2,“3”)应返回undefined。 addTogether(2)([3])应返回undefined。

我尽我所能,但唯一有用的,据称是迄今为止最好的解决方案如下:

function addTogether() {
  "use strict";
  // check if argument(s) valid number
  var validateNum = function(num) {
    if(typeof num !== 'number') {
      return undefined;
    } else
      return num;
  };
  // is there is one argument or two
  if(arguments.length > 1) {
    var a = validateNum(arguments[0]);
    var b = validateNum(arguments[1]);
    if(a === undefined || b === undefined) {
      return undefined;
    } else {
      return a + b;
    }
  // if only one argument, return function that expects one argument and returns sum.
  } else {
    var c = arguments[0];
    // start here
    if(validateNum(c)) {
      return function(arg2) {
        if(c === undefined || validateNum(arg2) === undefined) {
          return undefined;
        } else {
          return c + arg2;
        }
      }; // belongs to return function(arg2) {}
    }
  }
}

addTogether(2)(3);

7 个答案:

答案 0 :(得分:1)

function addTogether(a, b) {
  if (typeof a == "number") {
    if (arguments.length == 1) {
      return b => addTogether(a, b);
    } else if (typeof b == "number") {
      return a + b;
    } 
  }
}

// as per OP's code
// returns 3
console.log("addTogether(1, 2) = " + addTogether(1, 2));
console.log("addTogether(1, 2, 3) = " + addTogether(1, 2, 3));
console.log("addTogether(1)(2) = " + addTogether(1)(2));
console.log("addTogether(1)(2, 3) = " + addTogether(1)(2, 3));
console.log("addTogether(1, 2, '3') = " + addTogether(1, 2, '3'));
console.log("addTogether(1)(2, '3') = " + addTogether(1)(2, '3'));
console.log("addTogether(1, 2, [3]) = " + addTogether(1, 2, [3]));
console.log("addTogether(1)(2, [3]) = " + addTogether(1)(2, [3]));
console.log("addTogether(1, 2, NaN) = " + addTogether(1, 2, NaN));
console.log("addTogether(1)(2, NaN) = " + addTogether(1)(2, NaN));
// returns NaN
console.log("addTogether(1, NaN) = " + addTogether(1, NaN));
console.log("addTogether(1)(NaN) = " + addTogether(1)(NaN));
// returns undefined
console.log("addTogether() = " + addTogether());
console.log("addTogether(1)() = " + addTogether(1)());
console.log("addTogether('1') = " + addTogether('1'));
console.log("addTogether(1, '2') = " + addTogether(1, '2'));
console.log("addTogether(1)('2') = " + addTogether(1)('2'));
console.log("addTogether(1, [2]) = " + addTogether(1, [2]));
console.log("addTogether(1)([2]) = " + addTogether(1)([2]));

已经提出了以下改进,但它们会改变OP代码的语义:

  • 如果undefinedab则返回NaN,因为NaN不是“有效号码”
  • 返回undefined如果提供的参数超过两个而不是静默删除它们(感谢@PatrickRoberts)

如果您不介意为e返回功能。 G。 addTogether('x'),使用:

function addTogether(a, b) {
  if (arguments.length == 1) {
    return b => addTogether(a, b);
  } else if (typeof a == "number" && typeof b == "number") {
    return a + b;
  }
}

这样,您将始终为一个参数返回function,并为两个或多个参数返回Numberundefined =更健壮的代码。

要获得ES5兼容性,如果您不介意addTogether(2)()返回某个功能,请将b => addTogether(a, b)替换为addTogether.bind(undefined, a)(感谢@PatrickRoberts)。

答案 1 :(得分:1)

您可以使用点差运算符来改善您的功能以及Arraysome等一些reduce函数:

这样addTogether可以接受多个参数。 如果使用一个参数调用addTogether,则返回的函数也可以使用多个参数调用。



let isNotNumber = number=> typeof number != 'number';

let addTogether = function(...numbers){
  if(!numbers.length) return;
  if(numbers.length == 1){
    if(isNotNumber(numbers[0])) return;
    return function(...otherNumbers){
      if(otherNumbers.some(isNotNumber)) return;
      return otherNumbers.reduce((prev, curr)=> prev + curr, numbers[0]);
    }
  } else {
    if(numbers.some(isNotNumber)) return;
    return numbers.reduce((prev, curr)=> prev + curr);
  }
}

// Will return a value
console.log(addTogether(1,2,3));
console.log(addTogether(1)(2,3));

// Will return undefined
console.log(addTogether(1, [2]));
console.log(addTogether(1)('2'));
console.log(addTogether(1)([2]));
console.log(addTogether());




答案 2 :(得分:1)

根据jscomplexity.org,OP的函数的圈复杂度为8,而下面的解决方案的圈复杂度为5(based on the Babel ES5 transpilation)。

此解决方案在功能上等同于OP的代码,请参阅以下测试:



'use strict';

function addTogether(...augends) {
  if (augends.slice(0, 2).every(value => typeof value === 'number')) {
    switch (augends.length) {
    case 0:
      return;
    case 1:
      return (addend) => addTogether(augends[0], addend);
    default:
      return augends[0] + augends[1];
    }
  }
}

// should work (returns 3)
console.log("addTogether(1, 2) = " + addTogether(1, 2));
console.log("addTogether(1, 2, 3) = " + addTogether(1, 2, 3));
console.log("addTogether(1)(2) = " + addTogether(1)(2));
console.log("addTogether(1)(2, 3) = " + addTogether(1)(2, 3));
console.log("addTogether(1, 2, '3') = " + addTogether(1, 2, '3'));
console.log("addTogether(1)(2, '3') = " + addTogether(1)(2, '3'));
console.log("addTogether(1, 2, [3]) = " + addTogether(1, 2, [3]));
console.log("addTogether(1)(2, [3]) = " + addTogether(1)(2, [3]));
console.log("addTogether(1, 2, NaN) = " + addTogether(1, 2, NaN));
console.log("addTogether(1)(2, NaN) = " + addTogether(1)(2, NaN));
// should return NaN (not sure if this "works" or not)
console.log("addTogether(1, NaN) = " + addTogether(1, NaN));
console.log("addTogether(1)(NaN) = " + addTogether(1)(NaN));
// should not work (returns undefined)
console.log("addTogether() = " + addTogether());
console.log("addTogether(1)() = " + addTogether(1)());
console.log("addTogether('1') = " + addTogether('1'));
console.log("addTogether(1, '2') = " + addTogether(1, '2'));
console.log("addTogether(1)('2') = " + addTogether(1)('2'));
console.log("addTogether(1, [2]) = " + addTogether(1, [2]));
console.log("addTogether(1)([2]) = " + addTogether(1)([2]));




作为参考,这是我的另一个解决方案,它禁止无关的参数,也测试文字NaN值(具有讽刺意味的是typeof "number")。 修改不幸的是,由于修复了测试用例console.log("addTogether(1)() = " + addTogether(1)());的实现,现在它的圈复杂度为7:



'use strict';

function addTogether(...augends) {
  if (augends.every(value => typeof value === 'number' && !isNaN(value))) {
    switch (augends.length) {
    case 1:
      return (addend, ...addends) => addTogether(augends[0], addend, ...addends);
    case 2:
      return augends[0] + augends[1];
    }
  }
}

// should work (returns 3)
console.log("addTogether(1, 2) = " + addTogether(1, 2));
console.log("addTogether(1)(2) = " + addTogether(1)(2));
// should not work (returns undefined)
console.log("addTogether() = " + addTogether());
console.log("addTogether(1)() = " + addTogether(1)());
console.log("addTogether('1') = " + addTogether('1'));
console.log("addTogether(1, 2, 3) = " + addTogether(1, 2, 3));
console.log("addTogether(1, '2') = " + addTogether(1, '2'));
console.log("addTogether(1)('2') = " + addTogether(1)('2'));
console.log("addTogether(1, [2]) = " + addTogether(1, [2]));
console.log("addTogether(1)([2]) = " + addTogether(1)([2]));
console.log("addTogether(1)(2, 3) = " + addTogether(1)(2, 3));
console.log("addTogether(1, 2, '3') = " + addTogether(1, 2, '3'));
console.log("addTogether(1)(2, '3') = " + addTogether(1)(2, '3'));
console.log("addTogether(1, 2, [3]) = " + addTogether(1, 2, [3]));
console.log("addTogether(1)(2, [3]) = " + addTogether(1)(2, [3]));
console.log("addTogether(1, 2, NaN) = " + addTogether(1, 2, NaN));
console.log("addTogether(1)(2, NaN) = " + addTogether(1)(2, NaN));
console.log("addTogether(1, NaN) = " + addTogether(1, NaN));
console.log("addTogether(1)(NaN) = " + addTogether(1)(NaN));




答案 3 :(得分:0)

为什么你不提供功能参数。我认为这是最简单的vay:

$(document).ready(function() {
    var playing = false;

    $('.audio-button').click(function() {
        $(this).toggleClass("playing");
        var song = $(this).find('audio').attr('src');
        var audio = new Audio(song);

        if (playing == false) {
            audio.play();
            playing = true;
        } else {
            audio.pause();
            playing = false;
        }
    });
});

答案 4 :(得分:0)

我想你可以这样做。如果只提供了1个参数,它将返回当前函数,但绑定第一个参数,这样你只需要提供额外的参数



'use strict';

function isNumeric(arg) {
  return typeof arg === 'number' && !isNaN(arg);
}

function sum(a, b) {
  if (arguments.length > 2) {
    return undefined;
  }
  if (isNumeric(a) && isNumeric(b)) {
    return a + b;
  }
  if (typeof b === 'undefined' && arguments.length === 1) {
    if (typeof a === 'undefined') {
      return undefined;
    } else if (isNumeric(a)) {
      return sum.bind(this, a); // returns the sum function with the a argument bound to the current a argument
    }
  }
  return undefined;
}

function test() {
  var args = '',
    result = sum,
    value;
  Array.prototype.forEach.call(arguments, function(argument) {
    value = argument;
    if (!argument) {
      argument = 'undefined';
    }
    args += '(';
    if (Array.isArray(argument)) {
      args += Array.prototype.join.call(argument, ', ');
    } else {
      args += argument.toString();
    }
    args += '}';
    if (typeof result === 'function') {
      if (Array.isArray(value)) {
        result = result.apply({}, value);
      } else {
        result = result.call({}, value);
      }
    }
  });
  console.log(`sum${args} = ${result}`);
}

test(2);
test([2, 3]);
test(2, 3);
test([2, undefined], 3);
test([2, "3"]);
test(2, [
  [3]
]);
test(2, "3");
test(2, [3]);
test([NaN, 2]);




答案 5 :(得分:0)

您应该阅读有关函数currying,bind,call和apply的内容。 Currying适用于ES6,但绑定,调用和应用运行无处不在。

MDN网站的详细信息 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind

简而言之,您正在寻找的解决方案是:

function addTogether(a,b) {
    return a+b;
}
console.log(typeof addTogether); //-->function
console.log(typeof addTogether.bind(null,2)); //-->function
console.log(addTogether.bind(null,2)(3));//5
console.log(addTogether(2,3)); //-->5

是的,这很简单,不是开玩笑!

答案 6 :(得分:0)

这还不算短,但是我克服了挑战,很高兴避免分享我的解决方案:

function addTogether(...args) {
  if (args.length == 0) {
    //there is no args provided to the function .. lets return undefined
    return undefined;
  } else {
    //so there are arguments provided to the function
    if (args.length >= 2) {
      //there are two or more arguments provided to the function, lets consider only the first two arguments
      if (typeof(args[0]) != "number" || typeof(args[1]) != "number") {
        //so at least one of the first two arguments is not a number, lets return undefined
        return undefined;
      } else {
        //so both of the first two arguments are numbers, lets return their sum
        return args[0] + args[1];
      }
    } else { //handling input of one argument
      if (typeof(args[0]) != "number") {
        //the only argument provided is not even a number, lets return undefined
        return undefined;
      } else {
        //lets return a function that will wait for another argument 'y' to be summed with the currently provided argument 'args[0]'
        return function(y) {
          //first lets check if the second argument newly provided is a number or not
          if (typeof(y) != "number") return undefined;
          else return args[0] + y;
        };
      }
    }
  }
}


console.log(addTogether(2, 3)); // should return 5.
console.log(addTogether(2)(3)); // should return 5.
console.log(addTogether(2, "3")); // should return undefined.
console.log(addTogether(2)([3])); // should return undefined.