你如何讨论任意arity的任何javascript函数?

时间:2013-09-27 03:27:39

标签: javascript functional-programming currying

假设我有一些功能:

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

我想将它变成“咖喱”形式(引文中,因为它本身并不完全是咖喱):

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

    }

}

上面的表单具有我想要的部分绑定行为:

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

现在我想将这个过程自动化为一些泛型函数curry,这样在给定任何未加干扰的函数(可能是其参数数量)的情况下,就会生成上述函数。但我不太确定如何实现它。

或者,如果可以自动创建以下表单,那么它也很有趣:

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

虽然绑定f看起来像这样:

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

所以这是非常笨拙和非惯用的,但创建上述形式对我来说似乎更可行。

现在可以通过某个函数生成上述任何一种形式,如果是,怎么做?

5 个答案:

答案 0 :(得分:5)

我相信你可以简单地使用Function.prototype.bind。这为您提供了所需的所有灵活性,您可以立即获得函数的结果,或者只是将另一个值推入参数,直到您决定执行。

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

您还可以使用辅助函数,如:

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

此外,这非常灵活,因为您不需要连锁,您可以重复使用相同的咖喱功能。

var sumOnePlus = curry(sum, 1);

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

答案 1 :(得分:3)

这是我的尝试:

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

这不是partial application(在bind method的JS中很容易),但是真正的功能currying。它适用于任意但固定的arity的任何功能。对于可变参数函数,您可能需要不同的执行触发器,可能在不再传递任何参数时,或者在@plalx'的答案中使用exec方法。

答案 2 :(得分:0)

这样的事情怎么样:

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

如果您想要一个可重复使用的功能(我想我无法准确说出您想要的内容),那么这将有效:

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

答案 3 :(得分:0)

函数的bind()方法让你绑定函数内的this以及绑定额外的参数。因此,如果您为null参数传递this,则可以使用bind()将参数调整到函数中。

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

检查Javascript Function bind()以获取使用bind()绑定参数的详细信息和交互式示例。

答案 4 :(得分:0)

请检查curry库。

无论有多少参数,它都可以将任何函数转换为咖喱。

示例:

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