Карринг в javascript для функции с n параметрами
Если f:: (a, b) -> c, мы можем определить curry(f), как показано ниже:
карри (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
Как мы реализуем универсальную функцию карри, которая принимает функцию с n параметрами?
5 ответов
Если я правильно понимаю, я думаю, что это способ использовать 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))
Если вы хотите сделать это в ES5, вот несколько более подробный метод:
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))
Предостережение: у меня нет функционального фона, поэтому моя терминология может быть немного неправильной.
Если под "карри" вы подразумеваете "создать новую функцию, которая будет вызывать оригинал с предварительно заполненными аргументами", общее решение в ES5 и более ранних версиях выглядит следующим образом (см. Комментарии):
// 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);
В ES2015+ мы можем использовать остальные аргументы вместо 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);
Есть простой способ карри sum
функция с неограниченными параметрами.
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());
это add
Функция будет запускаться каждый раз, когда вы вызываете функцию карри с другим параметром. Как и в случае с const six = four + two;
Он возвращает значение из двух предыдущих вызовов, и цепочка продолжается и продолжается.
Имейте в виду, что для получения примитивного значения нам нужно вызвать .valueOf()
,
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))
Ниже приведено решение, вдохновленное решением Хуана Себастьяна Гайтана. Я только что расширил его для следующих случаев:
- добавить (1, 2)(2, 3)(1, 2, 3, 4).valueOf();
- Добавить (1,2,3,4).valueOf ();
- Добавить (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
Поскольку вывод является функцией, вам нужно valueOf(). чтобы получить значение. функция выглядит немного громоздкой из-за.reduce (), но ее очень просто читать.
Это хороший пример рекурсивной функции и функции карри.