如何在未知数量的参数上柯里化一个函数

How to curry a function across an unknown number of parameters

假设我有一个名为 multiplyDivide

的函数

如果我调用 multiplyDivide(2)(3)(4)(6) 它将等同于 2 * 3 / 4 * 6.

更新: 如果我事先不知道我将采用多少参数,是否可以编写这样的函数?例如,我可以有 multiplyDivide(1)(2)multiplyDivide(1)(2)(3)(4)...(n-1)(n)

你的意思是这样的吗?

var multiplyDivide = function(first){
  return function(second){
    return function(third){
      return function(forth){
        return first * second / third * forth;
      }
    }
  }
}
//should be 6
console.log(multiplyDivide(2)(4)(4)(3));

使用函数式方法,您可以创建一个函数,该函数 "curries" 为另一个函数提供参数。您将需要一种方法来告诉函数 return 值,因此在这种情况下,调用函数而不传递任何参数将 return 结果:

function curry(fn, ...values) {
    return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}

这个函数最酷的地方在于你可以传递多个参数and/or继续调用函数(1)(2, 3, 4)(5)

这里有几个例子:

function curry(fn, ...values) {
  return (...next) => (next.length) ? curry(fn, ...values, ...next) : fn(...values);
}

function multiplyDivide(...args) {
  return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}

let x = curry(multiplyDivide)(2)(3, 4)(6)();
console.log(x);

let y = curry(multiplyDivide)(5, 4, 2)(3);
y = y(3, 5)(1)();
console.log(y);

当然,这个例子确实暗示只是简单地重载 multiplyDivide 函数并在您准备好时将您的值传递给它:

function multiplyDivide(...args) {
  return args.reduce((total, next, i) => (i % 2) ? (total / next) : (total * next), args.shift());
}

const values = [5, 4, 2, 3, 3];
values.push(5, 1);

console.log(multiplyDivide(...values));

有点可能,但您需要定义终止条件,因为该问题本质上与编写递归函数的问题相同。该函数需要一种方法来判断它应该 return 一个函数还是一个值。

你如何表达对价值观的需求取决于你。一种方法是检查是否传递了参数:

// Using add instead of multiplyDivide to simplify example:

function add (num) {
    function adder (n) {
        if (n !== undefined) {
            num += n;
            return adder;
        }
        else { // terminate
            return num;
        }
    }
    return adder;
}

现在你可以做:

var sum = add(1)(2)(3)(4)();

否则它将return一个你可以继续调用的函数:

var x = add(1)(2)(3)(4);
x = x(5)(6)(7);
x = x(8)(9)(10);

var sum = x();

由于在js中函数是对象,所以也可以将值getter实现为静态方法。它不会纯粹是功能性的,但会使 "API" 更明确和更易于阅读:

function add (num) {
    function adder (n) {
        num += n;
        return adder;
    }
    adder.value = function(){
        return num
    };
    return adder;
}

您可以这样做:

var sum = add(1)(2)(3)(4).value();

您甚至可以通过重写内置的 .valueOf().toString() 方法来获得乐趣:

function add (num) {
    function adder (n) {
        num += n;
        return adder;
    }
    adder.valueOf = function(){
        return num
    };
    adder.toString = function(){
        return '' + num
    };
    return adder;
}

您可以这样做:

var sum = add(1)(2)(3)(4) + 5; // results in 15
var txt = add(1)(2)(3)(4) + "hello"; // results in "10hello"

这里的关键是您需要一种方法来告诉函数停止 returning 函数。

这其实是个很好的问题...

柯里化是函数式编程语言最基本的方面之一,您可以在其中传递 return 函数。 JS在某种程度上是一种函数式编程语言,应该可以执行此操作。

所以就像你问题中的那个一样,出于这个或那个原因,人们可能想柯里化一个无限多的参数函数。

OK 让你的功能成为

function multiplyDivide (n,m,o,p){
  return n * m / o * p;
}

那么实现实用程序 curry 函数以获得任何给定函数的柯里化版本的方法如下;

var curry = f => f.length ? (...a) => curry(f.bind(f,...a)) : f(),

让我们看看实际效果

function multiplyDivide (n,m,o,p){
      return n * m / o * p;
    }

var curry = f => f.length ? (...a) => curry(f.bind(f,...a)) : f(),
    f     = curry(multiplyDivide);
 
 res1 = f(4,5,2,10),
 res2 = f(4)(5,2,10),
 res3 = f(4)(5)(2,10),
 res4 = f(4)(5)(2)(10),
 res5 = f(4,5)(2,10),
 res6 = f(4,5)(2)(10),
 res7 = f(4,5,2)(10),
 res8 = f(4,5)(2,10);
console.log(res1,res2,res3,res4,res5,res6,res7,res8);

可能有更简单的方法,但这是我能想到的。

function addValues(a, b) {
   if(b!=undefined)
    return a + b;

   return function(b) {
       return a + b;
   }
}
let output1 = addValues(2)(4);  // 6
let output2 = addValues(2,1);   // 3
console.log(output1);
console.log(output2)

function multiply(a) {
   return function(b) {
       return b ? multiply(a*b) : a;
   }
}

console.log(multiply(2)());
console.log(multiply(2)(3)());
console.log(multiply(2)(3)(4)());
console.log(multiply(2)(3)(4)(5)());
function add() {
let args = [...arguments];
  function addAll(){
   let args1 = [...arguments];
   return add(...args, ...args1);
 }

let total = args.reduce((total, value) => total+value);
addAll.value = total;
return addAll;
}

console.log(add(2)(3)(4).value); 
console.log(add(2)(3).value);