问题
If f :: (a, b) -> c, we can define curry(f) as below:
curry(f) :: ((a, b) -> c) -> a -> b -> c
const curry = f => a => b => f(a, b);
const sum = curry((num1, num2) => num1 + num2);
console.log(sum(2)(3)); //5
How do we implement generic curry function that takes a function with n parameters?
回答1:
If I understand correctly, I think this is the way to go using ES6:
const curry = f => {
const nargs = f.length;
const vargs = [];
const curried = (...args) => vargs.push(...args) >= nargs
? f(...vargs.slice(0, nargs))
: curried;
return curried;
};
const fn2 = curry((a, b) => a + b);
const fn3 = curry((a, b, c) => a * (b + c));
const fn4 = curry((a, b, c, d) => Math.pow(a, b * (c + d)));
console.log(fn2(1)(2)); // 1 + 2
console.log(fn3(2)(3)(4)); // 2 * (3 + 4)
console.log(fn4(2)(1, 3)(4)); // 2 ^ (1 * (3 + 4))
If you want to do this in ES5, here's a slightly more verbose method:
function curry (f) {
var nargs = f.length;
var vargs = [];
return function curried () {
return vargs.push.apply(vargs, arguments) >= nargs
? f.apply(undefined, vargs.slice(0, nargs))
: curried;
};
}
var fn2 = curry(function (a, b) {
return a + b;
});
var fn3 = curry(function (a, b, c) {
return a * (b + c);
});
var fn4 = curry(function (a, b, c, d) {
return Math.pow(a, b * (c + d));
});
console.log(fn2(1)(2)); // 1 + 2
console.log(fn3(2)(3)(4)); // 2 * (3 + 4)
console.log(fn4(2)(1, 3)(4)); // 2 ^ (1 * (3 + 4))
回答2:
Caveat: I don't have a functional background, so my terminology may be a bit off.
If by "curry" you mean "create a new function that will call the original with some arguments pre-filled," the general solution in ES5 and earlier is as follows (see comments):
// Add a function to the function prototype
Object.defineProperty(Function.prototype, "curry", {
value: function() {
// Remember the original function
var f = this;
// Remember the curried arguments
var args = Array.prototype.slice.call(arguments);
// Return a new function that will do the work
return function() {
// The new function has been called: Call the original with
// the curried arguments followed by any arguments received in
// this call, passing along the current value of `this`
return f.apply(this, args.concat(Array.prototype.slice.call(arguments)));
};
}
});
// Usage:
function foo(a, b, c) {
console.log(a, b, c);
}
var f = foo.curry(1, 2);
f(3);
In ES2015+, we can use rest args instead of arguments
:
// REQUIRES ES2015+ support in your browser!
// Add a function to the function prototype
Object.defineProperty(Function.prototype, "curry", {
value: function(...curriedArgs) {
// Remember the original function
let f = this;
// Return a new function that will do the work
return function(...args) {
// The new function has been called: Call the original with
// the curried arguments followed by any arguments received in
// this call, passing along the current value of `this`
return f.apply(this, curriedArgs.concat(args));
};
}
});
// Usage:
function foo(a, b, c) {
console.log(a, b, c);
}
let f = foo.curry(1, 2);
f(3);
回答3:
ES6/2015
const curry = fn => function curried(cargs) {
return cargs.length >= fn.length ? fn.apply(this, cargs) : (...args) => curried([...cargs, ...args])
}([]);
const arg2 = curry((a, b) => a + b);
const arg3 = curry((a, b, c) => a * (b + c));
const arg4 = curry((a, b, c, d) => Math.pow(a, b * (c + d)));
console.log(arg2(1)(2)); // 1 + 2
console.log(arg3(2)(3)(4)); // 2 * (3 + 4)
console.log(arg4(2)(1, 3)(4)); // 2 ^ (1 * (3 + 4))
ES5
var curry = function(fn) {
var args = Array.prototype.slice.call(arguments);
if (args.length - 1 >= fn.length) return fn.apply(this, args.slice(1));
return function() {
return curry.apply(this, args.concat.apply(args, arguments));
};
};
var arg2 = curry(function(a, b) {
return a + b;
});
var arg3 = curry(function(a, b, c) {
return a * (b + c);
});
var arg4 = curry(function(a, b, c, d) {
return Math.pow(a, b * (c + d));
});
console.log(arg2(1)(2)); // 1 + 2
console.log(arg3(2)(3)(4)); // 2 * (3 + 4)
console.log(arg4(2)(1, 3)(4)); // 2 ^ (1 * (3 + 4))
回答4:
There's a simple way to curry your sum
function with unlimited parameters.
const add = (a) => {
const next = b => add(a + b);
next.valueOf = () => a
return next;
};
const one = add(1);
console.log(one.valueOf());
const two = one + 1;
console.log(two);
const four = two + two;
console.log(four)
const six = add(four)(two);
console.log(six.valueOf());
const eleven = six(4)(1);
console.log(eleven.valueOf());
This add
function would run every time you call the curried function with another parameter. Like in the case for const six = four + two;
It returns the value from two previous calls and the chain goes on and on.
Keep in mind that in order to get the primitive value we need to call .valueOf()
.
回答5:
Below is a solution inspired by Juan Sebastián Gaitán's solution I have just extended it for below cases:
- add(1, 2)(2, 3)(1, 2, 3, 4).valueOf();
- add(1,2,3,4).valueOf();
- add(1)(2)(3)(4)(5).valueOf();
const add = (a, ...rest) => {
a += rest.reduce((total, val) => {
return total + val;
}, 0);
const next = (...b) => add(a + b.reduce((total, val) => {
return total + val;
}, 0));
next.valueOf = () => a;
//console.log('a', a, '; next: ', next, '; rest: ', ...rest);
return next;
};
console.log(add(1, 2)(2, 3)(1, 2, 3, 4).valueOf()); //18
console.log(add(1,2,3,4).valueOf()); //10
console.log(add(1)(2)(3)(4)(5).valueOf()); //15
As the output is a function, you need valueOf(). to get the value. the function looks little cumbersome because of .reduce() but its very simple to read.
This is a good example of recursive function and a currying function.
来源:https://stackoverflow.com/questions/38638644/currying-in-javascript-for-function-with-n-parameters