JavaScript curry:有哪些实际应用?

IT技术 javascript function currying partial-application
2021-02-05 10:54:42

我想我还没有了解咖喱。我明白它的作用,以及如何去做。我只是想不出我会使用它的情况。

你在 JavaScript 中在哪里使用柯里化(或者主要库在哪里使用它)?欢迎 DOM 操作或一般应用程序开发示例。

其中一个答案提到了动画。像这样的函数slideUpfadeIn将一个元素作为参数,通常是一个柯里化函数,返回带有默认内置“动画函数”的高阶函数。为什么这比仅应用具有某些默认值的高层函数更好?

使用它有什么缺点吗?

这里有一些关于 JavaScript 柯里化的好资源:

当它们出现在评论中时,我会添加更多。


因此,根据答案,柯里化和部分应用通常是方便的技术。

如果您经常通过使用相同的配置调用高级函数来“提炼”高级函数,则可以使用柯里化(或使用 Resig 的部分)高级函数来创建简单、简洁的辅助方法。

6个回答

是使用闭包的 JavaScript 中柯里化的一个有趣且实用的用法

function converter(toUnit, factor, offset, input) {
    offset = offset || 0;
    return [((offset + input) * factor).toFixed(2), toUnit].join(" ");
}

var milesToKm = converter.curry('km', 1.60936, undefined);
var poundsToKg = converter.curry('kg', 0.45460, undefined);
var farenheitToCelsius = converter.curry('degrees C', 0.5556, -32);

milesToKm(10);            // returns "16.09 km"
poundsToKg(2.5);          // returns "1.14 kg"
farenheitToCelsius(98);   // returns "36.67 degrees C"

这依赖于 的curry扩展名Function,尽管您可以看到它只使用apply(没什么特别的):

Function.prototype.curry = function() {
    if (arguments.length < 1) {
        return this; //nothing to curry with - return function
    }
    var __method = this;
    var args = toArray(arguments);
    return function() {
        return __method.apply(this, args.concat([].slice.apply(null, arguments)));
    }
}
从我读到的(刚才)来看,“curry”通常不是函数技巧包的一部分,除非您使用 Prototype 库或自己添加它。不过很酷。
2021-03-20 10:54:42
有趣,但这个例子似乎不起作用。offset+inputundefined + 1.60936在您的milesToKm示例中;结果是NaN.
2021-03-23 10:54:42
@Nathan - 偏移量不能未定义 - 默认为 0
2021-03-23 10:54:42
使用 ES5 bind() 方法也可以实现相同的效果。Bind 创建一个新函数,该函数在调用时使用其第一个参数的上下文和随后的参数序列(在任何传递给新函数之前)调用原始函数。所以你可以做... var milesToKm = converter.bind(this, 'km',1.60936); 或 var farenheitToCelsius = converter.bind(this, 'degrees C',0.5556, -32); 第一个参数,上下文,this,在这里无关紧要,所以你可以传递 undefined。当然,对于非 ES5 回退,您需要使用自己的绑定方法来扩充基本函数原型
2021-03-26 10:54:42
这很棒!我看到它类似于 lisp 引用,上面写着“Lisp 是一种可编程的编程语言”
2021-04-13 10:54:42

@汉克盖伊

回应 EmbiggensTheMind 的评论:

我想不出在 JavaScript柯里化本身很有用的例子它是一种将具有多个参数的函数调用转换为每个调用具有单个参数的函数调用链的技术,但 JavaScript 支持在单个函数调用中使用多个参数。

然而,在 JavaScript 中——我假设大多数其他实际语言(不是 lambda 演算)——它通常与部分应用程序相关联。John Resig解释得更好,但要点是有一些逻辑可以应用于两个或多个参数,并且您只知道其中一些参数的值。

您可以使用部分应用程序/柯里化来修复那些已知值并返回一个仅接受未知数的函数,以便稍后在您实际拥有要传递的值时调用。这提供了一种很好的方法来避免重复自己,当您一遍又一遍地使用所有相同的值调用相同的 JavaScript 内置函数时。窃取约翰的例子:

String.prototype.csv = String.prototype.split.partial(/,\s*/);
var results = "John, Resig, Boston".csv();
alert( (results[1] == "Resig") + " The text values were split properly" );
@ftor先生,您的回答非常糟糕。咖喱显然是为了让函数更美味。你显然没有抓住重点。
2021-03-21 10:54:42
这真的是一个糟糕的答案。柯里化与部分应用无关。柯里化使函数组合成为可能。函数组合使函数重用成为可能。函数的重用增加了代码的可维护性。就这么简单!
2021-03-31 10:54:42
经常有人说柯里化对函数组合很有用,但如果我的所有组合函数都采用一个参数,我可以编写一个不需要柯里化的组合函数。当一个或多个函数接受多个参数时,需要使用柯里化。
2021-04-07 10:54:42

同意 Hank Gay - 它在某些真正的函数式编程语言中非常有用 - 因为它是必要的部分。例如,在 Haskell 中,您根本无法将多个参数带入一个函数——您无法在纯函数式编程中做到这一点。您一次接受一个参数并构建您的函数。在 JavaScript 中,它是完全没有必要的,尽管像“转换器”这样的人为例子。这是相同的转换器代码,无需柯里化:

var converter = function(ratio, symbol, input) {
    return (input*ratio).toFixed(2) + " " + symbol;
}

var kilosToPoundsRatio = 2.2;
var litersToUKPintsRatio = 1.75;
var litersToUSPintsRatio = 1.98;
var milesToKilometersRatio = 1.62;

converter(kilosToPoundsRatio, "lbs", 4); //8.80 lbs
converter(litersToUKPintsRatio, "imperial pints", 2.4); //4.20 imperial pints
converter(litersToUSPintsRatio, "US pints", 2.4); //4.75 US pints
converter(milesToKilometersRatio, "km", 34); //55.08 km

我非常希望 Douglas Crockford 在“JavaScript: The Good Parts”中提到了柯里化的历史和实际使用,而不是他随意的评论。在读完这篇文章后的很长一段时间里,我一直很困惑,直到我学习函数式编程并意识到这就是它的来源。

经过更多思考,我认为在 JavaScript 中柯里化有一个有效的用例:如果您尝试使用 JavaScript 使用纯函数式编程技术进行编写。不过,这似乎是一个罕见的用例。

你的代码比零号囚徒的代码更容易理解,它解决了同样的问题,无需柯里化或任何复杂的东西。你有 2 个竖起大拇指,而他几乎有 100 个。算一下。
2021-04-06 10:54:42

我发现类似于 python 的函数functools.partial在 JavaScript 中更有用:

function partial(fn) {
  return partialWithScope.apply(this,
    Array.prototype.concat.apply([fn, this],
      Array.prototype.slice.call(arguments, 1)));
}

function partialWithScope(fn, scope) {
  var args = Array.prototype.slice.call(arguments, 2);
  return function() {
    return fn.apply(scope, Array.prototype.concat.apply(args, arguments));
  };
}

为什么要使用它?您想要使用 this 的一种常见情况是当您想要this在函数中绑定到一个值时:

var callback = partialWithScope(Object.function, obj);

现在当回调被调用时,this指向obj. 这在事件情况下或节省一些空间很有用,因为它通常会使代码更短。

柯里化与部分类似,区别在于柯里化返回的函数只接受一个参数(据我所知)。

这不是魔术或任何东西......只是匿名函数的一个愉快的速记。

partial(alert, "FOO!") 相当于 function(){alert("FOO!");}

partial(Math.max, 0) 对应于 function(x){return Math.max(0, x);}

对部分的调用(MochiKit术语。我认为其他一些库为函数提供了一个 .curry 方法,它做同样的事情)看起来比匿名函数更好,噪音更少。