首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带函数构造函数的Curry函数

带函数构造函数的Curry函数
EN

Code Review用户
提问于 2015-08-27 11:46:01
回答 1查看 626关注 0票数 5

我应该这样做一个咖喱函数,还是最好避免函数构造函数的任何方式?我想知道的是,这是否是“糟糕的做法”,以及如何才能进行其他的赛跑。例如,可以使用Function.bind将参数绑定到函数,但它更像是部分应用程序。递归会对性能产生影响吗?

代码语言:javascript
复制
var curry = function(fn) {
    var fnString = fn.toString();
    var arity = fn.length;
    var paramList = fnString.substring(
            fnString.indexOf("(") + 1, fnString.indexOf(")")).split(",");
    var body = fnString.substring(
            fnString.indexOf("return"), fnString.indexOf("}"));
    var newBody = paramList.slice(1).reduce(function(p,c) {
        return p + "return function(" + c + ") {";
    },"") + body + ( new Array(arity).join("}") );

    return new Function(paramList[0], newBody);

 };

var test = function(a,b,c,d) {
    return a * b * c * d;
};

var curriedTest = curry( test );
console.log( curriedTest(1)(2)(3)(4) ); \\-> 24
EN

回答 1

Code Review用户

发布于 2015-08-27 19:41:52

这里的问题并不是Function构造函数本身(尽管这不是一个好主意):恐怕会引起麻烦的是代码的其余部分。

您正在丢弃几乎整个函数体,并且只保留return行。只有第一条return行在那里。

如果将test函数改为如下所示:

代码语言:javascript
复制
var test = function(a,b,c,d) {
  var product = a * b * c * d;
  return product;
};

您只会得到一个错误,因为curried函数变成了:

代码语言:javascript
复制
function(a) {
  return function(b) {
    return function(c) {
      return function(d) {
        return product; // ReferenceError
      }
    }
  }
}

如果有嵌套函数,它也会中断,例如:

代码语言:javascript
复制
var test = function(a,b,c,d) {
  function getRandomNumber() {
    return Math.random() * 10;
  }

  return a * b * c * d * getRandomNumber();
};

因为您的代码只查看第一个return行,因此咖喱函数最终只能是return Math.random() * 10

而且在return line之前不能有任何紧凑型大括号,所以即使是这样的东西也会崩溃:

代码语言:javascript
复制
var test = function(a,b,c,d) {
  if(a == 1) {
    console.log("got: a == 1");
  }
  return a * b * c * d;
};

因为if的}return之前,所以最终将错误的东西从函数的身体中分割出来。这会导致new Function调用由于语法错误而失败。

类似地,没有return语句的函数将导致问题:

代码语言:javascript
复制
var test = function(a,b,c,d) {
  console.log(a * b * c * d);
};

代码中的奇怪评论也会让您感到困惑:

代码语言:javascript
复制
var test = function(a,b,c,d /* more args, y'know */) { ...

哦,而且您也没有传播上下文,所以在每一层嵌套中,this可能不会引用正确的/预期的对象。

最后,length并不是一种很好的检查兼容性的方法。函数可以是可变的,使用任意数量的参数,无论是否列出(例如,见下文)。

所以,不,这不是个好办法。此外,Function构造函数只是伪装的eval,应该避免。只需使用bind,或如下所示的函数。

通常的方法是将目标函数包装为-原样。类似于:

代码语言:javascript
复制
function curry(func, args) {
  var spice = [].slice.call(arguments, 1);

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

这使您可以执行如下操作:

代码语言:javascript
复制
var curried = curry(test, 1, 2, 3);
curried(4); // => 24

它适用于上面所有的test函数变体。

如果您确实坚持重写代码和递归嵌套,那么我所能提供的最好的方法(这不是一个好的解决方案)就是根本不重写函数的主体。

当前的解决方案基本上创建了嵌套闭包,这意味着函数的主体可以完全保持不变,只有它的参数必须改变。它将成为最内部最嵌套的闭包。

代码语言:javascript
复制
var curry = function(fn) {
  var source = fn.toString(),
      args = source.match(/\((.*?)\)/)[1].trim().split(/\s*,\s*/),
      body = source.replace(/(^.*?\{|\}[^\}]*$)/g, "");

  return args.reverse().reduce(function (nested, arg) {
    return new Function(arg, "return " + nested.toString())
  }, new Function(args.shift(), body));
};

它产生的功能如下:

代码语言:javascript
复制
function anonymous(a) {
  return function anonymous(b) {
    return function anonymous(c) {
      return function anonymous(d) {
        // original function body
      }
    }
  }
}

有效率?不是的。强壮吗?该死的不!我不建议你用它。

但是,对于上面列出的一些例子来说,它会工作得更好。它仍然会被奇怪的评论所窒息,它不做上下文传播,不能处理变化论,它基本上是一个脆弱的大黑客。但至少它可以处理局部变量、嵌套函数、附加的大括号和缺少的return语句。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/102066

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档