使用 ES5,如何柯里化一个带有无限参数的函数。
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
这是我想出的解决方案:
function add(a, b, c) {
var args = Array.prototype.slice.call(arguments);
return function () {
var secondArgs = Array.prototype.slice.call(arguments);
var totalArguments = secondArgs.concat(args);
var sum = 0;
for (i = 0; i < totalArguments.length; i++) {
sum += totalArguments[0];
}
return sum;
}
}
但是,有人告诉我,它的风格不太“实用”。
与上面的问题类似。通过递归计算第 n 层柯里化之和
技巧:为了停止递归,我将最后一个 () 作为空白传递**
function sum(num1) {
return (num2) => {
if(!num2) {
return num1;
}
return sum(num1 + num2);
}
}
console.log('Sum :', sum(1)(2)(3)(4)(5)(6)(7)(8)())
您的
add
函数不是很“实用”,部分原因是它试图做的不仅仅是将传递给它的数字相加。如果其他开发人员查看您的代码,看到一个 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!
此代码现在可以在任何地方重复使用。
您可以使用该柯里化函数来创建其他柯里化函数引用,而无需任何额外的麻烦。
坚持数学主题,假设我们有一个乘法函数,可以将传递给它的所有数字相乘:
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
这种函数柯里化方法允许您将该方法应用于任何函数,而不仅仅是数学函数。尽管提供的
curry
函数不支持所有边缘情况,但它为您的问题提供了一个实用且简单的解决方案,可以轻松构建。
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;
}
如果您只想要一级柯里化,那么我会这样做:
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;
};
}
现在,这是一个更通用的解决方案,具有无限级别的柯里化:
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
函数返回另一个可以被视为数字的函数(例如,您可以执行2 * running(21)
)。但是,因为它也是一个函数,所以您可以应用它(例如,您可以执行 running(21)(21)
)。它之所以有效,是因为 JavaScript 使用 valueOf
方法自动将对象强制转换为基元。
此外,
running
生成的函数是递归柯里化的,允许您将其多次应用到任意数量的参数。
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
方法来手动将 valueOf
的结果强制转换为数字。
柯里化的无限和,您可以传递单个参数或多个直至无限:
function adding(...arg) {
return function clousureReturn(...arg1) {
if (!arguments.length) {
let finalArr = [...arg, ...arg1];
let total = finalArr.reduce((sum, ele) => sum + ele);
return total;
}
return adding(...arg, ...arg1)
}
}
这是单参数无限柯里化的解决方案
function sum(x) {
return function(y) {
if (y !== undefined) {
return sum(x + y);
}
return x;
}
}
sum(1)(2)(3)(4)() //10
无限参数柯里化的解决方案
let sum = function (...args1) {
let total =args1.reduce((total,num) => total + num,0)
return function(...args2) {
if(args2.length!== 0) {
let total2 = args2.reduce((total,num)=>total + num,0);
return sum(total,total2);
}
return total;
};
};
console.log(sum(2,3,4)(2,3)(1)()); // 15
还有一个
简单可变参数函数
function sum() {
let args = [...arguments];
let total = args.reduce((total,num) => total + num,0);
return total;
}
console.log(sum(1,2,3,4)) // 10
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 版本的 curry 没有那么漂亮,因为缺少
...
运算符:
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
,而是显式将其复制到新数组。
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;
}
const add = (one) => { // one: Parameter passed in test
return (...args) => {
// args: Array with all the parameters passed in test
return one + args.reduce((sum, i) => sum + i, 0) // using reduce for doing sum
}
}
var test = add(1);
console.log(test(2)); //should return 3
console.log(test(2, 3)); //should return 6
console.log(test(4, 5, 6)); //should return 16