JavaScript咖喱:有哪些实际应用?

Dav*_*lan 171 javascript function currying partial-application

我不认为我已经讨好了.我理解它的作用,以及如何做到这一点.我想不出我会用它的情况.

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

其中一个答案提到了动画.函数如slideUp,fadeIn将元素作为参数,通常是一个curried函数,返回高阶函数,内置默认的"动画函数".为什么这比仅使用某些默认值更高的函数更好?

使用它有什么缺点吗?

根据要求,这里有一些关于JavaScript currying的好资源:

我会在评论中添加更多内容.


因此,根据答案,currying和部分应用一般是便利技术.

如果您经常通过使用相同配置调用高级函数来"精炼"高级函数,则可以使用更高级别的函数来设置(或使用Resig的部分)来创建简单,简洁的帮助器方法.

Pri*_*ERO 112

以下是使用闭包的JavaScript中currying的一个有趣且实用的用法:

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"
Run Code Online (Sandbox Code Playgroud)

这依赖于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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 使用ES5 bind()方法可以实现相同的效果.Bind创建一个新函数,当调用它时,调用原始函数的第一个参数的上下文和随后的参数序列(在任何传递给新函数之前).所以你可以做... var milesToKm = converter.bind(this,'km',1.60936); 或var farenheitToCelsius = converter.bind(this,'degrees C',0.5556,-32); 第一个参数,上下文,这在这里是无关紧要的,所以你可以通过undefined.当然,您需要使用自己的绑定方法为非ES5回退扩充基本Function原型 (11认同)
  • 从我刚才读到的内容来看,"咖喱"通常不是功能包的一部分,除非你使用Prototype库或自己添加它.不过很酷. (6认同)
  • 这很棒!我认为它类似于lisp引用"Lisp是一种可编程编程语言" (5认同)
  • @Nathan - 偏移量不能未定义 - 默认为0 (3认同)
  • 有趣,但这个例子似乎不起作用.你的`milesToKm`例子中`offset + input`将是`undefined + 1.60936`; 这会导致'NaN`. (2认同)
  • 您是否可以包含用于使此示例更完整的咖喱功能? (2认同)

Han*_*Gay 33

@Hank Gay

回应EmbiggensTheMind的评论:

我想不出一个实例,其中curry -by本身 - 在JavaScript中很有用; 它是一种将具有多个参数的函数调用转换为函数调用链的技术,每个调用都有一个参数,但JavaScript在单个函数调用中支持多个参数.

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

您可以使用部分应用程序/ currying来修复这些已知值并返回仅接受未知数的函数,以便在您实际拥有要传递的值时调用.这提供了一种很好的方法来避免重复自己,当你一直使用所有相同的值而一次调用相同的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" );
Run Code Online (Sandbox Code Playgroud)

  • 这真的是一个糟糕的答案.Currying与部分应用无关.卷曲使功能组合成为可能.功能组合使功能重用.重用函数可提高代码的可维护性.就这么简单! (6认同)
  • 我试图用[我的书面文章中的一些示例和演示来解释它.](http://conceptf1.blogspot.com/2014/03/currying-in-javascript.html) (2认同)
  • @ftor先生,你是一个非常糟糕的答案.Currying显然是让功能更美味.你显然错过了这一点. (2认同)

Arm*_*her 7

我发现类似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));
  };
}
Run Code Online (Sandbox Code Playgroud)

你为什么要用它?要使用此this功能的常见情况是,您希望将函数绑定到值:

var callback = partialWithScope(Object.function, obj);
Run Code Online (Sandbox Code Playgroud)

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

Currying类似于partial,不同之处在于currying返回的函数只接受一个参数(据我所知).


Byr*_*atz 6

同意Hank Gay - 它在某些真正的函数式编程语言中非常有用 - 因为它是必要的部分.例如,在Haskell中,您根本无法将多个参数带到函数中 - 您无法在纯函数式编程中执行此操作.你一次拿一个参数并建立你的功能.在JavaScript中,尽管有像"转换器"这样的人为例子,但它根本就没有必要.这是相同的转换器代码,无需curry:

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
Run Code Online (Sandbox Code Playgroud)

我非常希望Douglas Crockford在"JavaScript:The Good Parts"中提到过currying的历史和实际使用,而不是他的不和之言.在读完之后的最长时间里,我感到很困惑,直到我学习功能编程并意识到它来自哪里.

经过一番思考之后,我认为在JavaScript中有一个有效的用例:如果你试图使用JavaScript编写纯函数式编程技术.看起来像一个罕见的用例.

  • 您的代码比Prisoner Zero的代码更容易理解,并且可以解决相同的问题,而不会引起麻烦或其他麻烦。你有2个大拇指,而他有近100个大拇指。 (2认同)
  • 好吧...... https://renomad.com/external_author_docs/parable_two_programmers.txt (2认同)

gio*_*gim 5

考虑filter功能。并且您想为其编写一个回调。

let x = [1,2,3,4,5,6,7,11,12,14,15];
let results = x.filter(callback);
Run Code Online (Sandbox Code Playgroud)

假设只想输出偶数,所以:

let callback = x => x % 2 === 0;
Run Code Online (Sandbox Code Playgroud)

现在想象我们想要实现callback这样的,根据场景,它输出高于某个阈值的偶数(这样的数字应该是可配置的)。

我们不能轻易地将这样的阈值数字作为callback函数的参数,因为filter调用callback并默认传递它数组元素和索引。

你会如何实施这个?

这是柯里化的一个很好的用例:

let x = [1,2,3,4,5,6,7,11,12,14,15];
let results = x.filter(callback);
Run Code Online (Sandbox Code Playgroud)