使用ES5,你如何理解一个带有无限参数的函数。
var socket = require('socket.io');
mysocket = socket.connect('http://192.168.1.70:3000');
mysocket.emit('deviceevent', { param1: "update", param2: "0", param3: "1" });
上面的函数只需要三个参数,但我们希望我们的curried版本能够获取无限参数。
因此,以下所有测试用例都应该通过:
function add(a, b, c) {
return a + b + c;
}
以下是我提出的解决方案:
var test = add(1);
test(2); //should return 3
test(2,3); //should return 6
test(4,5,6); //should return 16
但是,有人告诉我,它的风格并不是很“实用”。
答案 0 :(得分:5)
partial
一个简单的解决方案是使用partial
,如下所示:
Function.prototype.partial = function () {
var args = Array.prototype.concat.apply([null], arguments);
return Function.prototype.bind.apply(this, args);
};
var test = add.partial(1);
alert(test(2)); // 3
alert(test(2,3)); // 6
alert(test(4,5,6)); // 16
function add() {
var sum = 0;
var length = arguments.length;
for (var i = 0; i < length; i++)
sum += arguments[i];
return sum;
}
如果你只想要一个级别的currying,那么这就是我要做的事情:
var test = add(1);
alert(test(2)); // 3
alert(test(2,3)); // 6
alert(test(4,5,6)); // 16
function add() {
var runningTotal = 0;
var length = arguments.length;
for (var i = 0; i < length; i++)
runningTotal += arguments[i];
return function () {
var sum = runningTotal;
var length = arguments.length;
for (var i = 0; i < length; i++)
sum += arguments[i];
return sum;
};
}
现在,这是一个更为通用的解决方案,具有无限的currying水平:
var add = running(0);
var test = add(1);
alert(+test(2)); // 3
alert(+test(2,3)); // 6
alert(+test(4,5,6)); // 16
function running(total) {
var summation = function () {
var sum = total;
var length = arguments.length;
for (var i = 0; i < length; i++)
sum += arguments[i];
return running(sum);
}
summation.valueOf = function () {
return total;
};
return summation;
}
running total是summation的中间结果。 running
函数返回另一个可被视为数字的函数(例如,您可以2 * running(21)
)。但是,因为它也是一个功能,你可以应用它(例如你可以running(21)(21)
)。它的工作原理是因为JavaScript使用valueOf
方法自动将对象强制转换为基元。
此外,running
生成的函数是递归curry,允许您根据需要多次应用它。
var resultA = running(0);
var resultB = resultA(1,2);
var resultC = resultB(3,4,5);
var resultD = resultC(6,7,8,9);
alert(resultD + resultD(10)); // 100
function running(total) {
var summation = function () {
var sum = total;
var length = arguments.length;
for (var i = 0; i < length; i++)
sum += arguments[i];
return running(sum);
}
summation.valueOf = function () {
return total;
};
return summation;
}
您唯一需要注意的是,有时您需要手动将running
的结果强制转换为数字,方法是将unary plus operator应用于该数字或调用其valueOf
方法直接。
答案 1 :(得分:5)
您的add
功能不是很好的部分原因&#34;功能性&#34;是因为它试图做的不仅仅是添加传递给它的数字。让其他开发人员查看你的代码,看到add
函数,并且当他们调用它时,会得到一个返回给它们的函数而不是总和。
例如:
//Using your add function, I'm expecting 6
add(1,2,3) //Returns another function = confusing!
功能方法是创建一个功能,允许您讨论任何其他功能,并简化您的add function
:
function curry(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return fn.apply(this, args.concat(
Array.prototype.slice.call(arguments, 0)
));
}
}
function add() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function (previousValue, currentValue) {
return previousValue + currentValue;
});
}
现在,如果你想要理解这个功能,你可以这样做:
var curry1 = curry(add, 1);
console.log(
curry1(2), // Logs 3
curry1(2, 3), // Logs 6
curry1(4, 5, 6) // Logs 16
);
//You can do this with as many arguments as you want
var curry15 = curry(add, 1,2,3,4,5);
console.log(curry15(6,7,8,9)); // Logs 45
如果我还想添加1, 2, 3
,我可以这样做:
add(1,2,3) //Returns 6, AWESOME!
此代码现在可以从任何地方重复使用。
你可以使用那个curry函数来制作其他curried函数引用,而不会有任何额外的麻烦。
坚持使用数学主题,假设我们有一个乘法函数乘以传递给它的所有数字:
function multiply() {
var args = Array.prototype.slice.call(arguments);
return args.reduce(function (previousValue, currentValue) {
return previousValue * currentValue;
});
}
multiply(2,4,8) // Returns 64
var curryMultiply2 = curry(multiply, 2);
curryMultiply2(4,8) // Returns 64
这种功能性的currying方法允许您将该方法应用于任何函数,而不仅仅是数学函数。虽然提供的curry
函数不支持所有边缘情况,但它为您的问题提供了一个功能简单的解决方案,可以轻松构建。
答案 2 :(得分:0)
通过定义一个curry函数,有更多的通用方法,它在评估内部函数时需要最少量的参数。让我先使用ES6(后来的ES5),因为它使它更透明:
var curry = (n, f, ...a) => a.length >= n
? f(...a)
: (...ia) => curry(n, f, ...[...a, ...ia]);
然后定义一个对所有参数求和的函数:
var sum = (...args) => args.reduce((a, b) => a + b);
然后我们可以讨论它,告诉它应该等到至少2个参数:
var add = curry(2, sum);
然后一切都适合:
add(1, 2, 3) // returns 6
var add1 = add(1);
add1(2) // returns 3
add1(2,3) // returns 6
add1(4,5,6) // returns 16
您甚至可以通过提供第一个参数来跳过创建add
:
var add1 = curry(2, sum, 1);
ES5版本的咖喱对于缺少...
运算符而言并不那么漂亮:
function curry(n, f) {
var a = [].slice.call(arguments, 2);
return a.length >= n
? f.apply(null, a)
: function () {
var ia = [].slice.call(arguments);
return curry.apply(null, [n, f].concat(a).concat(ia));
};
}
function sum() {
return [].slice.call(arguments).reduce(function (a, b) {
return a + b;
});
};
其余的都一样......
注意:如果需要考虑效率,您可能不想在slice
上使用arguments
,而是明确地将其复制到新数组。
答案 3 :(得分:0)
这个游戏有点晚,但这是我的两分钱。基本上,这利用了一个事实,即函数也是JavaScript中的对象。
function add(x) {
if (x === undefined) {
return add.numbers.reduce((acc, elem) => acc + elem, 0);
} else {
if (add.numbers) {
add.numbers.push(x);
} else {
add.numbers = [x];
}
}
return add;
}