JavaScript карри: каковы практические приложения? - PullRequest
169 голосов
/ 22 сентября 2008

Я не думаю, что я еще не начал карри. Я понимаю, что он делает, и как это сделать. Я просто не могу придумать ситуацию, в которой бы я ее использовал.

Где вы используете каррирование в JavaScript (или где его используют основные библиотеки)? Приветствуются манипуляции с DOM или общие примеры разработки приложений.

Один из ответов упоминает анимацию. Такие функции, как slideUp, fadeIn, принимают элемент в качестве аргументов и обычно являются карри-функцией, возвращающей функцию высокого порядка со встроенной «функцией анимации» по умолчанию. Почему это лучше, чем просто применять функцию более высокого уровня с некоторыми значениями по умолчанию?

Есть ли недостатки в его использовании?

В соответствии с просьбой, вот несколько хороших ресурсов по каррированию JavaScript:

Я добавлю больше по мере появления в комментариях.


Итак, согласно ответам, карри и частичное применение в целом являются удобными техниками.

Если вы часто «уточняете» высокоуровневую функцию, вызывая ее с одинаковой конфигурацией, вы можете каррировать (или использовать частичку Resig) высокоуровневую функцию для создания простых, лаконичных вспомогательных методов.

Ответы [ 15 ]

112 голосов
/ 28 июля 2011

Вот интересное И практическое использование каррирования в 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)));
    }
}
33 голосов
/ 22 сентября 2008

@ Хэнк Гей

В ответ на комментарий EmbiggensTheMind:

Я не могу вспомнить случай, когда curry - сам по себе - полезен в JavaScript; Это методика преобразования вызовов функций с несколькими аргументами в цепочки вызовов функций с одним аргументом для каждого вызова, но JavaScript поддерживает несколько аргументов в одном вызове функции.

В JavaScript - и я предполагаю, что большинство других реальных языков (не лямбда-исчисление) - обычно это связано с частичным применением. Джон Резиг объясняет это лучше , но суть в том, что есть некоторая логика, которая будет применяться к двум или более аргументам, и вы знаете значение (я) только для некоторых из этих аргументов.

Вы можете использовать частичное приложение / каррирование, чтобы исправить эти известные значения и вернуть функцию, которая принимает только неизвестные, которая будет вызвана позже, когда у вас действительно будут значения, которые вы хотите передать. Это обеспечивает отличный способ избежать повторения, когда вы бы снова и снова вызывали одни и те же встроенные 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" );
7 голосов
/ 22 сентября 2008

Я нашел функции, которые напоминают 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 в функции со значением:

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

Теперь, когда вызывается обратный вызов, this указывает на obj. Это полезно в ситуациях события или для экономии места, поскольку обычно делает код короче.

Карринг похож на частичный с тем отличием, что функция, которую возвращает карринг, принимает только один аргумент (насколько я понимаю).

5 голосов
/ 03 сентября 2015

Согласие с Хэнком Гей - Это чрезвычайно полезно в некоторых настоящих функциональных языках программирования - потому что это необходимая часть. Например, в 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

Мне бы очень хотелось, чтобы Дуглас Крокфорд в «JavaScript: Хорошие части» немного упомянул историю и фактическое использование карри, а не свои посторонние замечания. Долгое время после прочтения я был ошеломлен, пока не изучал функциональное программирование и не понял, откуда он взялся.

Подумав еще немного, я полагаю, что есть один правильный вариант использования для карри в JavaScript: если вы пытаетесь писать, используя чисто функциональные методы программирования с использованием JavaScript. Похоже на редкий случай использования.

2 голосов
/ 24 июля 2010

Вот пример.

Я обрабатываю несколько полей с помощью JQuery, чтобы видеть, что делают пользователи. Код выглядит так:

$('#foo').focus(trackActivity);
$('#foo').blur(trackActivity);
$('#bar').focus(trackActivity);
$('#bar').blur(trackActivity);

(Для пользователей, не являющихся пользователями JQuery, я говорю, что всякий раз, когда пара полей получает или теряет фокус, я хочу, чтобы вызывалась функция trackActivity (). Я также мог бы использовать анонимную функцию, но я бы дублировать его 4 раза, поэтому я вытащил его и назвал.)

Теперь выясняется, что одно из этих полей нужно обрабатывать по-разному. Я хотел бы иметь возможность передать параметр в одном из этих вызовов для передачи в нашу инфраструктуру отслеживания. С карри я могу.

2 голосов
/ 22 сентября 2008

Это не волшебство или что-то еще ... просто приятное сокращение для анонимных функций.

partial(alert, "FOO!") эквивалентно function(){alert("FOO!");}

partial(Math.max, 0) соответствует function(x){return Math.max(0, x);}

Призывы к частичной ( MochiKit терминологии. Я думаю, что некоторые другие библиотеки предоставляют функциям метод .curry, который делает то же самое), выглядят немного лучше и менее шумными, чем анонимные функции.

1 голос
/ 22 июля 2015

Я знаю его старый поток, но мне придется показать, как это используется в библиотеках javascript:

Я буду использовать библиотеку lodash.js для конкретного описания этих понятий.

Пример:

var fn = function(a,b,c){ 
return a+b+c+(this.greet || ‘'); 
}

Частичное применение:

var partialFnA = _.partial(fn, 1,3);

Карри:

var curriedFn = _.curry(fn);

Связывание:

var boundFn = _.bind(fn,object,1,3 );//object= {greet: ’!'}

использование:

curriedFn(1)(3)(5); // gives 9 
or 
curriedFn(1,3)(5); // gives 9 
or 
curriedFn(1)(_,3)(2); //gives 9


partialFnA(5); //gives 9

boundFn(5); //gives 9!

разница:

после карри мы получаем новую функцию без предустановленных параметров.

после частичного применения мы получаем функцию, которая связана с некоторыми предварительно связанными параметрами.

в привязке мы можем связать контекст, который будет использоваться для замены «this», если не привязано, по умолчанию для любой функции будет область окна.

Совет: нет необходимости изобретать велосипед. Частичное применение / связывание / каррирование очень тесно связано. Вы можете увидеть разницу выше. Используйте это значение где угодно, и люди поймут, что вы делаете, без проблем с пониманием, плюс вам придется использовать меньше кода.

1 голос
/ 20 марта 2011

JavaScript функции называются lamda на другом функциональном языке. Его можно использовать для создания нового API (более мощной или полной функции), основанного на простом вводе другого разработчика. Карри - только одна из техник. Вы можете использовать его для создания упрощенного API для вызова сложного API. Если вы разработчик, который использует упрощенный API (например, вы используете JQuery для простых манипуляций), вам не нужно использовать карри. Но если вы хотите создать упрощенный API, карри - ваш друг. Вы должны написать фреймворк javascript (например, jQuery, mootools) или библиотеку, тогда вы сможете оценить его мощь. Я написал расширенную функцию карри на http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html. Вам не нужен метод curry для выполнения карри, он просто помогает выполнять карри, но вы всегда можете сделать это вручную, написав функцию A () {} для возврата другой функции B () {}. Чтобы сделать его более интересным, используйте функцию B () для возврата другой функции C ().

1 голос
/ 22 сентября 2008

Я бы сказал, что, скорее всего, все библиотеки анимации в JS используют каррирование. Вместо того, чтобы передавать для каждого вызова набор затронутых элементов и функцию, описывающую, как должен вести себя элемент, в функцию более высокого порядка, которая будет обеспечивать все функции синхронизации, как правило, клиенту легче выпустить, как некоторые общедоступные API. такие функции, как «slideUp», «fadeIn», которые принимают только элементы в качестве аргументов, и это всего лишь некоторая карри-функция, возвращающая функцию высокого порядка со встроенной «функцией анимации» по умолчанию.

1 голос
/ 22 сентября 2008

Что касается библиотек, использующих его, всегда есть Функциональный .

Когда это полезно в JS? Вероятно, в то же время он полезен для других современных языков, но я вижу, как я использую его только в сочетании с частичным применением.

...