JavaScript curry: какие практические приложения?

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

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

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

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

В соответствии с запросом здесь приведены некоторые полезные ресурсы по каррированию JavaScript:

Добавьте еще больше, когда они появятся в комментариях.


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

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

Ответ 1

@Hank Gay

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

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

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

Вы можете использовать частичное приложение /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" );

Ответ 2

Здесь интересное и практическое использование currying в 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)));
    }
}

Ответ 3

Я нашел функции, похожие на 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);

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

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

Ответ 4

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

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

partial (Math.max, 0) соответствует функции (x) {return Math.max(0, x);}

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

Ответ 5

Вот пример.

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

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

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

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

Ответ 6

Согласие с 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

Я очень хочу, чтобы Дуглас Крокфорд в "JavaScript: Хорошие детали" упомянул об истории и фактическом использовании карри, а не о своих небрежных высказываниях. В течение долгого времени после прочтения этого я был ошеломлен, пока не изучал функциональное программирование и не понял, откуда он пришел.

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

Ответ 7

Как для библиотек, использующих его, всегда Functional.

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

Я буду использовать библиотеку lodash.js, чтобы описать эти понятия конкретно.

Пример:

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

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

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

Currying:

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, если не связанный по умолчанию какой-либо функцией будет область окна.

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

Ответ 11

Я согласен, что время от времени вы хотели бы получить мяч, создавая псевдофункцию, которая всегда будет иметь значение первого аргумента. К счастью, я столкнулся с новой библиотекой JavaScript, называемой jPaq (h ttp://jpaq.org/), который предоставляет эту функцию. Самое лучшее в библиотеке - это то, что вы можете загрузить свою собственную сборку, которая содержит только тот код, который вам понадобится.

Ответ 12

Я просто написал пример jPaq, который показывает некоторые интересные приложения функции curry. Посмотрите здесь: Currying Up String Functions

Ответ 13

Просто хотел добавить некоторые ресурсы для Functional.js:

Лекция/конференция, объясняющая некоторые приложения http://www.youtube.com/watch?v=HAcN3JyQoyY

Обновлена ​​библиотека Functional.js: https://github.com/loop-recur/FunctionalJS Некоторые хорошие помощники (извините, но здесь нет репутации: p): /Петли RECUR/PreludeJS

Я недавно использовал эту библиотеку, чтобы уменьшить повторение в библиотеке помощников js IRC-клиентов. Это отличный материал - действительно помогает очистить и упростить код.

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

Ответ 14

Вы можете использовать встроенное связывание для быстрого однострочного решения

function clampAngle(min, max, angle) {
    var result, delta;
    delta = max - min;
    result = (angle - min) % delta;
    if (result < 0) {
        result += delta;
    }
    return min + result;
};

var clamp0To360 = clampAngle.bind(null, 0, 360);

console.log(clamp0To360(405)) // 45

Ответ 15

Еще один удар по нему, работая с promises.

(Отказ от ответственности: JS noob, исходящий из мира Python. Даже там currying не используется так много, но иногда это может пригодиться. Поэтому я обрезал функцию currying - см. ссылки)

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

Я изменил свой живой URL-адрес, чтобы сделать это неудачным.

function ajax_batch(e){
    var url = $(e.target).data("url");

    //induce error
    url = "x" + url;

    var promise_details = $.ajax(
        url,
        {
            headers: { Accept : "application/json" },
            // accepts : "application/json",
            beforeSend: function (request) {
                if (!this.crossDomain) {
                    request.setRequestHeader("X-CSRFToken", csrf_token);
                }
        },
        dataType : "json",
        type : "POST"}
    );
    promise_details.then(notify_batch_success, fail_status_specific_to_batch);
}

Теперь, чтобы сообщить пользователю, что пакет завершился неудачно, мне нужно написать эту информацию в обработчике ошибок, потому что все, что он получает, является ответом с сервера.

У меня все еще есть информация, доступная во время кодирования - в моем случае у меня есть несколько возможных партий, но я не знаю, какая из них потерпела неудачу w.o. разбора ответа сервера о неудавшемся URL-адресе.

function fail_status_specific_to_batch(d){
    console.log("bad batch run, dude");
    console.log("response.status:" + d.status);
}

Позвольте сделать это. Выход консоли:

Приставки:

bad batch run, dude utility.js (line 109) response.status:404

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

    ... rest is as before...
    var target = $(e.target).text();
    var context = {"user_msg": "bad batch run, dude.  you were calling :" + target};
    var contexted_fail_notification = curry(generic_fail, context); 

    promise_details.then(notify_batch_success, contexted_fail_notification);
}

function generic_fail(context, d){
    console.log(context);
    console.log("response.status:" + d.status);
}

function curry(fn) {
     var slice = Array.prototype.slice,
        stored_args = slice.call(arguments, 1);
     return function () {
        var new_args = slice.call(arguments),
              args = stored_args.concat(new_args);
        return fn.apply(null, args);
     };
}

Приставки:

Object { user_msg="bad batch run, dude. you were calling :Run ACL now"} utility.js (line 117) response.status:404 utility.js (line 118)

В более общем плане, учитывая, насколько широкое использование обратного вызова в JS, каррирование кажется довольно полезным инструментом.

https://javascriptweblog.wordpress.com/2010/04/05/curry-cooking-up-tastier-functions/ http://www.drdobbs.com/open-source/currying-and-partial-functions-in-javasc/231001821?pgno=2