使用n个参数在javascript中进行函数的currying

问题描述 投票:0回答:5

如果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个参数的函数的通用咖喱函数?

javascript functional-programming currying curry
5个回答
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))

2
投票

警告:我没有功能背景,所以我的术语可能有些偏差。

如果用“curry”表示“创建一个新函数,它将调用原始函数并预先填充一些参数”,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 +中,我们可以使用rest args而不是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);

0
投票

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

0
投票

有一种简单的方法可以用无限的参数来调整你的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());

每次用另一个参数调用curried函数时,这个add函数都会运行。就像const six = four + two;It的情况一样,返回前两个调用的值,链继续。

请记住,为了获得原始值,我们需要调用.valueOf()


-1
投票

以下是JuanSebastiánGaitán解决方案的解决方案,我刚刚针对以下案例进行了扩展:

  • add(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(),函数看起来很麻烦,但它的读取非常简单。

这是递归函数和currying函数的一个很好的例子。

© www.soinside.com 2019 - 2024. All rights reserved.