问题
Problem: Create a function that sums two arguments together. If only one argument is provided, then return a function that expects one argument and returns the sum.
For example, addTogether(2, 3) should return 5, and addTogether(2) should return a function.
Calling this returned function with a single argument will then return the sum: var sumTwoAnd = addTogether(2); sumTwoAnd(3) returns 5.
If either argument isn't a valid number, return undefined.
Solution should return:
addTogether(2, 3) should return 5. addTogether(2)(3) should return 5. addTogether(2, "3") should return undefined. addTogether(2)([3]) should return undefined.
I tried everything I could, but the only thing that worked, and is purportedly the best solution so far is the following:
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);
回答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]));
The following improvements have been suggested, but they would change the semantics of OPs code:
- return
undefined
ifa
orb
isNaN
, asNaN
is not a 'valid number' - return
undefined
if more than two arguments are provided instead of silently dropping them (thanks @PatrickRoberts)
If you don't mind returning a function for e. g. addTogether('x')
, use:
function addTogether(a, b) {
if (arguments.length == 1) {
return b => addTogether(a, b);
} else if (typeof a == "number" && typeof b == "number") {
return a + b;
}
}
This way, your will always return a function
for one argument and Number
or undefined
for two or more arguments = more robust code.
For ES5 compatibility and if you don't mind addTogether(2)()
returning a function, replace b => addTogether(a, b)
with addTogether.bind(undefined, a)
(thanks @PatrickRoberts).
回答2:
You can use spread operator for improving your function and some Array
functions like some
or reduce
:
In that way addTogether
can accept more than one argument.
If addTogether
is called with one argument, the function returned can be called with more than one arguments too.
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());
回答3:
According to jscomplexity.org, OP's function has a cyclomatic complexity of 8 while the solution below have a cyclomatic complexity of 5 (based on the Babel ES5 transpilation).
This solution is functionally equivalent to OP's code, see tests below:
'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]));
For reference, here is my other solution which prohibits extraneous arguments and also tests against literal NaN
values (which ironically are typeof "number"
). Edit Unfortunately due to fixing the implementation for the test case console.log("addTogether(1)() = " + addTogether(1)());
, it now has a cyclomatic complexity of 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));
回答4:
Why you don't provide arguments to function. I think that this is the simplest vay:
function add(arg1, arg2) {
if (isNan(arg1) || isNan(arg2)) return undefined;
if (!arg2) {
return functionName(arg1);
}
return arg1 + arg2;
}
回答5:
You should read about function currying, bind, call, and apply. Currying is applicable to ES6, but bind,call and apply run everywhere.
Details on the MDN site https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
In short, the solution you are looking for is:
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
Yeah, it IS that simple, not kidding!
回答6:
I guess you could do it like this. In case only 1 argument is provided, it would return the current function, but bind the first argument, so that you only have to provide the extra argument
'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]);
来源:https://stackoverflow.com/questions/37835934/is-possible-to-reduce-the-complexity-and-spaghetti-quality-of-this-javascript-al