How to curry a function across an unknown number of parameters

百般思念 提交于 2019-11-28 13:44:22

Why does it have to be curried? Wouldn't this be much easier to just "overload" a function by supplying n arguments?

function multiplyDivide() {
  var result = arguments[0];
  for (var i = 1; i < arguments.length; i++) {
    result = (i % 2) ? (result * arguments[i]) : (result / arguments[i]);
  }
  return result;
}

console.log(multiplyDivide(2, 3, 4, 6)); //9
console.log(multiplyDivide(5, 4, 2, 3, 3, 5, 1)); //50

It's sort of possible but you need to define the terminating condition because the problem is essentially the same problem as writing a recursive function. The function needs a way to tell weather it should return a function or a value.

How you signal the need for values is up to you. One way of doing it is to check if an argument is passed:

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

Now you can do:

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

Otherwise it would return a function which you can keep calling:

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

var sum = x();

Since in js functions are objects, you can also implement the value getter as a static method. It won't be purely functional but makes the "API" a bit more explicit and easier to read:

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

Which would allow you to do:

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

You can even get fancy by overriding the built-in .valueOf() and .toString() methods:

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

Which would allow you to do:

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

The key here is that you need a way to tell the function to stop returning functions.

Do you mean something like this?

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

This is in fact a very good question...

Currying is one of the most fundamental aspects of functional programming languages where you can pass around and return functions. JS being a functional programming language at some level, should be able to perform this operation.

So just like the one in your question one might want to curry an indefinitely many argument function for this or that reasons.

OK let your function be

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

Then a way of implementing a utility curry function to obtain the curried version of any given function would be as follows;

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

So let's see it in action

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

There might be simpler ways but this is what i could have come up with.

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!