8

我正在尝试创建可应用于任何函数并返回另一个函数的curry 函数,其中应用了 1 个参数。我想拥有的属性:

  1. 如果函数只有一个参数,curry 函数应该返回值:f(a); 咖喱(f,x)= f(x);
  2. 如果函数有很多参数,currey 应该重新运行 curried 函数:g(a1,a2,..,aN); 咖喱(g,x) = g2(a2,..,aN) : g2(a2,..aN)=g(x,a2,...,aN)
  3. curry 函数的长度属性应该“根据需要”工作 g.length = N => curry(g,x).length = N-1

Prototype Framework 中有一些 curry 的实现,在一篇博客中有讨论。但是这种实现并不好,因为它不适用于只有一个参数 (1) 的函数,并且返回函数“长度”属性为 0 (3)。

对于第一个属性,有一个简单的实现:

 function curry(f,x) {
    if (f.length == 1) return f(x);
    ...
 }

但我不知道如何使用第三条规则,即函数可以构造为内部函数,因为会有一个嵌套的词法环境并且能够使用 f:

function curry(f,x) {
   return function() { ... }
}

但在这种情况下,我将不再能够显式设置参数。另一方面,可以使用“new Function”语句构造函数,就像这样:

 function curry(f,x) {
    var args = [];
    for (var i=1; i<f.length; i++) {
       args.push('a'+i);
    }
    var sa = args.join();
    return new Function(sa,"return f(x,"+sa+")");
 }

但在这种情况下,f 和 x 将解除绑定,因为匿名函数将在全局词法环境中创建。

所以问题:

  1. 有没有办法在使用 function 关键字创建函数时显式设置参数计数?
  2. 有没有办法设置使用“新功能”语句创建的功能环境?
  3. 我们有办法以任何其他方式解决我的问题吗?
4

4 回答 4

6

函数库实现的方式是将传入“curry()”的参数作为第一个要传递的参数。然后,“curry”操作的函数结果将在调用它时获取传入的任何附加参数,并将它们添加到参数列表的末尾。它根本不担心参数列表的长度,因为这在 JavaScript 中通常不是固定的,所以真的没有意义。

因此:

var curry = myFunction.curry("Tuesday", x + y);

所以调用:

curry(100, true);

就像打电话一样:

myFunction("Tuesday", x + y, 100, true);

Functional 还有另一个称为“partial()”的函数,它允许更受控制的参数替换。当您调用“partial()”时,您传入一个虚拟参数(“_”)来指示“孔”在参数列表中的位置:

var partialFunc = myFunction.partial("Tuesday", _, 100, true, _, "banana");

这两个“_”参数意味着生成的“partialFunc”应该将传递给它的前两个参数放入参数列表中的这些槽中:

partialFunc(x + y, "Texas");

因此就像调用:

myFunction("Tuesday", x + y, 100, true, "Texas", "banana");

我衷心建议获取该库并查看所涉及的代码。出乎意料的简洁明了。

还有一件事:重要的是要注意,因为 JavaScript 不是惰性求值语言,所以这与 Haskell 等惰性函数式语言中的“curry”操作并不完全相同。区别在于“咖喱时间”的参数被评估,因此有点“煮熟”到结果中。在懒惰的语言中,事情是不同的。

于 2011-03-11T13:34:00.457 回答
3
function curry(fn, args) {
  // no need to var these, they are scoped via argument list - we overwrite them
  // convert the arguments to a real array:
  args = [].slice.apply(arguments);
  // first argument is a function:
  fn = args.shift();
  return function() {
    // get internal args
    var iArgs = [].slice.apply(arguments);
    // apply curried arguments, then our arguments:
    return fn.apply(this, args.concat(iArgs));
  }
}

function add(a,b) { return a+b; }
var add2 = curry(add, 2);
alert(add2(5)); //7

var hello = curry(add, "Hello ");
alert(hello("World!"));
于 2011-03-11T13:39:35.030 回答
2

多年来,我一直使用 curry 的函数原型,如下所示:

Function.prototype.curry = function curry() {
    var fn = this, args = Array.prototype.slice.call(arguments);
    return function curryed() {
        return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
    };
};

也许它也会满足您的需求。

您只需像这样使用它:

function fn1(arg1,arg2) { /*...*/ }

var fn1Curried = fn1.curry('whatever'); //sets arg1

它适用于任意数量的参数。

于 2011-03-11T14:05:43.210 回答
0
function curry(func) {
    var initial_args = [].slice.apply(arguments, [1]);
    var func_args_length = func.length;

    function curried(args) {
        if (args.length >= func_args_length) {
            return func.apply(null, args);
        }

        return function () {
            return curried(args.concat([].slice.apply(arguments)));
        };
    }

    return curried(initial_args);
}
于 2012-12-26T20:08:32.193 回答