Вопрос

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

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

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

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

По запросу вот несколько хороших ресурсов по каррированию в JavaScript:

Добавлю еще по мере появления в комментариях.


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

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

Это было полезно?

Решение

@Хэнк Гей

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

Я не могу вспомнить случая, когда каррирование— сам по себе — полезен в 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" );

Другие советы

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

Я нашел функции, похожие на функции 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.Это полезно в ситуациях, связанных с событиями, или для экономии места, поскольку обычно это делает код короче.

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

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

Вот пример.

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

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

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

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

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

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

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

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

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

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

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

Функции 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().

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

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

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

Я только что написал пример jPaq, который показывает несколько интересных применений функции карри.Проверьте это здесь: Карринг строковых функций

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

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

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

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

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

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

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

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

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

Во-первых, я начинаю с вызова 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);
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top