8

假设我有一些功能:

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

所以它非常笨拙且不习惯,但创建上述形式对我来说似乎更可行。

现在是否可以通过某个函数生成上述任何形式,如果可以,如何生成?

4

5 回答 5

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;
于 2013-09-27T03:49:37.210 回答
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);
    };
}

这不是部分应用(在 JS 中使用bind方法很容易),而是真正的函数式柯里化。它适用于任意但固定数量的任何函数。对于可变参数函数,您可能需要一个不同的执行触发器,也许是当不再传递任何参数或exec@plalx 答案中的方法时。

于 2013-09-28T14:39:03.510 回答
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
于 2013-09-27T03:37:43.423 回答
0

函数上的bind()方法允许您绑定this函数内部以及绑定额外参数。因此,如果您传递参数,则可以使用null将参数柯里化到函数中。thisbind()

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()以获取使用绑定参数的详细信息和交互式示例。

于 2013-09-27T04:10:54.603 回答
0

请检查咖喱库。

无论有多少参数,它都可以将任何函数变成咖喱。

例子:

> 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
>
于 2016-07-05T05:29:37.780 回答