最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

functional programming - Currying in javascript for function with n parameters - Stack Overflow

programmeradmin6浏览0评论

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?

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?

Share Improve this question edited Oct 10, 2020 at 0:00 imz -- Ivan Zakharyaschev 5,0716 gold badges55 silver badges108 bronze badges asked Jul 28, 2016 at 13:58 Jyoti Prasad PalJyoti Prasad Pal 1,6293 gold badges29 silver badges42 bronze badges 5
  • Do you mean f :: ((a, b) -> c) -> ( a-> b-> c)? – Patrick Roberts Commented Jul 28, 2016 at 14:01
  • @Patrick Roberts yes – Jyoti Prasad Pal Commented Jul 28, 2016 at 14:14
  • 1 I love these currying challenges. I should write a library or something with a bunch of currying utilities, maybe people would find it worth using. – Patrick Roberts Commented Jul 28, 2016 at 14:21
  • 1 I have accepted and upvoted. – Jyoti Prasad Pal Commented Jul 28, 2016 at 17:23
  • Where is the option to accept? I have clicked on up arrow on left side of your answer. – Jyoti Prasad Pal Commented Jul 28, 2016 at 17:26
Add a ment  | 

5 Answers 5

Reset to default 5

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))

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 ments):

// 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);

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))

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().

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.

发布评论

评论列表(0)

  1. 暂无评论