Frage

Let's say I have some function:

function g(a,b,c){ return a + b + c }

And I'd like to turn it into its "curried" form (in quotations since it's not exactly curried per se):

function h(a,b,c){

    switch(true){

        case (a !== undefined && b !== undefined && c !== undefined):
            return a + b + c

        case (a !== undefined && b !== undefined && c === undefined): 
            return function(c){ return a + b + c }

        case (a !== undefined && b == undefined  && c === undefined ):
            return function(b,c){
                return (c === undefined) ? function(c){ return a + b + c } : a + b + c
            }

        default:
            return h

    }

}

The above form has the partial binding behavior I want:

h(1)     -> h(b,c)
h(1,2)   -> h(c)
h(1,2,3) -> 6
h()      -> h(a,b,c)

Now I'd like to automate this process into some generic function curry such that given any un-curried function (and maybe its number of parameters), the above function is generated. But I'm not quite sure how to implement it.

Alternatively, if the following form could be automatically created, it'd be also interesting:

function f(a,b,c){
    return function(a){ return function(b){ return function(c){ return a + b + c }}}
}

Though binding f looks like this:

f(1)(2)(3) = 6

so it is very unwieldily and non-idiomatic, but creating the above form seem more feasible to me.

Now is could any of the above form be generated by some function, if so, how?

War es hilfreich?

Lösung

I believe that you could simply use Function.prototype.bind. That gives you all the flexibility you need, wheter you want the result of the function right away or simply push another value into the arguments until you decide to execute.

function sum() {
    return [].reduce.call(arguments, function (c, n) {
        return c + n;
    });
}

sum(1, 2); //3

var sum2 = sum.bind(null, 1, 2);

sum2(); //3

var sum3 = sum2.bind(null, 3);

sum3(); //6

You could also use a helper function like:

function curry(fn) {
    var c = curry.bind(this, fn = fn.bind.apply(fn, [this].concat([].slice.call(arguments, 1))));

    c.exec = fn;

    return c;
}

curry(sum, 1, 2)(3)(4, 5)(6, 7, 8).exec(); //36

Also this is very flexible as you do not have to chain, you can re-use the same curried function.

var sumOnePlus = curry(sum, 1);

sumOnePlus.exec(2); //3;
sumOnePlus.exec(3); //4;

Andere Tipps

Here's my attempt:

function curry(fn, len) {
    if (typeof len != "number")
        len = fn.length; // getting arity from function
    return function curried() {
        var rlen = len - arguments.length;
        if (rlen <= 0) // then execute now
            return fn.apply(this, arguments);
        // else create curried, partially bound function:
        var bargs = [this]; // arguments for `bind`
        bargs.push.apply(bargs, arguments);
        return curry(fn.bind.apply(fn, bargs), rlen);
    };
}

This does not partial application (which is easy in JS with the bind method), but true functional currying. It works with any functions of arbitrary, but fixed arity. For variadic functions you would need a different execution trigger, maybe when no arguments are passed any more or an exec method like in @plalx' answer.

How about something like this:

function makeLazy(fn) {
    var len = fn.length;
    var args = [];
    return function lazy() {
        args.push.apply(args, arguments);
        if (args.length < len) {
            return lazy;
        } else {
            return fn.apply(this, args);
        }
    }
}

function f(a,b,c) { return a + b + c; }

var lazyF = makeLazy(f);
lazyF(1)(2)(3); // 6

var lazyF = makeLazy(f);
lazyF(1,2)(3); // 6

If you wanted a reusable function (I guess I can't tell exactly what you want), then this would work:

function makeCurry(fn) {
    return function curry() {
        var args = [].slice.call(arguments);
        return function() {
            return fn.apply(this, args.concat.apply(args, arguments));
        };
    }
}


function f(a,b,c) { return a + b + c; }

var curryF = makeCurry(f);
var addOneTwoAnd = curryF(1,2);

addOneTwoAnd(3); // 6
addOneTwoAnd(6); // 9

Please check the curry library.

It can turn any function into curry no matter how many arguments are there.

Example:

> var curry = require('curry');
undefined
> var add = curry(function(a, b, c, d, e) { return a + b + c + d + e; });
undefined
> add(1)
[Function]
> add(1,2,3,4,5)
15
> add(1,2)(3,4)(5)
15
>

The bind() method on function lets you bind the this inside the function as well as bind extra parameters. So, if you pass null for the this parameter, you can use bind() to curry the parameters into the function.

function g(a,b,c){ return a + b + c }

var g_1 = g.bind(null, 1);
console.log(g_1(2, 3)); // Prints 6

var g_1_2 = g.bind(null, 1, 2);
console.log(g_1_2(3)); // Prints 6

Check Javascript Function bind() for details and interactive examples of using bind() to bind parameters.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top