سؤال

لا أعتقد أنني قد تأثرت بالكاري بعد.أنا أفهم ما يفعله، وكيفية القيام بذلك.لا أستطيع التفكير في موقف سأستخدمه فيه.

أين تستخدم الكاري في JavaScript (أو أين تستخدمه المكتبات الرئيسية)؟نرحب بمعالجة DOM أو أمثلة تطوير التطبيقات العامة.

واحدة من الإجابات يذكر الرسوم المتحركة.وظائف مثل slideUp, fadeIn تأخذ عنصرًا كوسيطات وعادةً ما تكون دالة متقنة تُرجع وظيفة الترتيب العالي مع "وظيفة الرسوم المتحركة" الافتراضية المضمنة.لماذا يعد ذلك أفضل من مجرد تطبيق الوظيفة الأعلى مع بعض الإعدادات الافتراضية؟

هل هناك أي عيوب لاستخدامه؟

كما هو مطلوب، إليك بعض الموارد الجيدة حول JavaScript Currying:

سأضيف المزيد عندما يظهرون في التعليقات.


لذا، وفقًا للإجابات، فإن الكاري والتطبيق الجزئي بشكل عام هي تقنيات مريحة.

إذا كنت تقوم في كثير من الأحيان "بتحسين" وظيفة عالية المستوى عن طريق استدعائها بنفس التكوين، فيمكنك استخدام وظيفة المستوى الأعلى (أو استخدام 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)));
    }
}

لقد وجدت وظائف تشبه وظائف بايثون 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.يعد هذا مفيدًا في مواقف الأحداث أو لتوفير بعض المساحة لأنه عادةً ما يجعل التعليمات البرمجية أقصر.

يشبه الكاري جزئيًا مع اختلاف أن الوظيفة التي ترجعها الكاري تقبل وسيطة واحدة فقط (بقدر ما أفهم ذلك).

الاتفاق مع هانك جاي - إنه مفيد للغاية في بعض لغات البرمجة الوظيفية الحقيقية - لأنه جزء ضروري.على سبيل المثال، في هاسكل، لا يمكنك ببساطة أخذ معلمات متعددة لوظيفة ما - ولا يمكنك القيام بذلك في البرمجة الوظيفية البحتة.أنت تأخذ معلمة واحدة في كل مرة وتقوم ببناء وظيفتك.في جافا سكريبت، هذا ببساطة غير ضروري، على الرغم من الأمثلة المفتعلة مثل "المحول".إليك نفس رمز المحول، دون الحاجة إلى الكاري:

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);}

النداءات الجزئية (MochiKit المصطلح.أعتقد أن بعض المكتبات الأخرى تعطي الوظائف طريقة .curry التي تفعل نفس الشيء) تبدو أجمل قليلاً وأقل ضوضاءً من الوظائف المجهولة.

أما بالنسبة للمكتبات التي تستخدمه، فهناك دائمًا وظيفي.

متى يكون مفيدًا في JS؟ربما يكون مفيدًا في نفس الوقت في اللغات الحديثة الأخرى، لكن المرة الوحيدة التي أرى نفسي أستخدمها فيها هي بالتزامن مع التطبيق الجزئي.

أود أن أقول إنه على الأرجح أن جميع مكتبات الرسوم المتحركة في JS تستخدم الكاري.بدلاً من الاضطرار إلى تمرير مجموعة من العناصر المتأثرة ووظيفة لكل استدعاء، مع وصف كيفية تصرف العنصر، إلى وظيفة ذات ترتيب أعلى تضمن جميع عناصر التوقيت، فمن الأسهل عمومًا على العميل إصدارها، مثل بعض واجهات برمجة التطبيقات العامة وظيفة مثل "slideUp" و"fadeIn" التي تأخذ العناصر فقط كوسيطات، وهي مجرد بعض الوظائف المتقنة التي تعيد وظيفة الترتيب العالي مع "وظيفة الرسوم المتحركة" الافتراضية المضمنة.

وظائف جافا سكريبت تسمى لامدا في لغة وظيفية أخرى.يمكن استخدامه لإنشاء واجهة برمجة تطبيقات جديدة (وظيفة أكثر قوة أو تعقيدًا) بناءً على مدخلات بسيطة لمطور آخر.الكاري هو مجرد واحدة من التقنيات.يمكنك استخدامه لإنشاء واجهة برمجة تطبيقات مبسطة لاستدعاء واجهة برمجة تطبيقات معقدة.إذا كنت المطور الذي يستخدم واجهة برمجة التطبيقات المبسطة (على سبيل المثال، إذا كنت تستخدم jQuery لإجراء معالجة بسيطة)، فلن تحتاج إلى استخدام الكاري.ولكن إذا كنت ترغب في إنشاء واجهة برمجة التطبيقات المبسطة، فإن الكاري هو صديقك.يجب عليك كتابة إطار عمل جافا سكريبت (مثل jQuery أو mootools) أو مكتبة، ثم يمكنك تقدير قوتها.لقد كتبت وظيفة الكاري المحسنة، في http://blog.semanticsworks.com/2011/03/enhanced-curry-method.html .لا تحتاج إلى طريقة الكاري للقيام بعملية الكاري، إنها تساعد فقط في إجراء الكاري، ولكن يمكنك دائمًا القيام بذلك يدويًا عن طريق كتابة دالة A(){} لإرجاع دالة أخرى B(){}.ولجعل الأمر أكثر إثارة للاهتمام، استخدم الدالة B() لإرجاع دالة أخرى C().

أعرف موضوعه القديم ولكن سيتعين علي توضيح كيفية استخدامه في مكتبات جافا سكريبت:

سأستخدم مكتبة 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!

اختلاف:

بعد الكاري نحصل على وظيفة جديدة بدون أي معلمات محددة مسبقًا.

بعد التطبيق الجزئي نحصل على دالة مرتبطة ببعض المعلمات المسبقة.

في الربط يمكننا ربط السياق الذي سيتم استخدامه ليحل محل "هذا"، إذا لم يكن ملزمًا افتراضيًا لأي وظيفة فسيكون نطاق النافذة.

ينصح:ليست هناك حاجة إلى إعادة اختراع العجلة.يرتبط التطبيق الجزئي/الربط/الكاري ارتباطًا وثيقًا.يمكنك رؤية الفرق أعلاه.استخدم هذا المعنى في أي مكان وسيتعرف الأشخاص على ما تفعله دون مشاكل في الفهم بالإضافة إلى أنه سيتعين عليك استخدام تعليمات برمجية أقل.

أوافق على أنه في بعض الأحيان ترغب في الحصول على الكرة من خلال إنشاء دالة زائفة سيتم دائمًا ملء قيمة الوسيطة الأولى بها.لحسن الحظ، وجدت مكتبة جافا سكريبت جديدة تمامًا تسمى jPaq (hhttps://jpaq.org/) الذي يوفر هذه الوظيفة.أفضل ما في المكتبة هو حقيقة أنه يمكنك تنزيل الإصدار الخاص بك والذي يحتوي فقط على الكود الذي ستحتاجه.

لقد كتبت للتو مثال jPaq الذي يوضح بعض التطبيقات الرائعة لوظيفة الكاري.تحقق من ذلك هنا: تجهيز وظائف السلسلة

أردت فقط إضافة بعض الموارد لـ Functional.js:

محاضرة/مؤتمر شرح بعض التطبيقاتhttp://www.youtube.com/watch?v=HAcN3JyQoyY

مكتبة Functional.js المحدثة:https://github.com/loop-recur/FunctionalJSبعض المساعدين اللطيفين (آسف جديد هنا، لا سمعة:p):/loop-recur/PreludeJS

لقد كنت أستخدم هذه المكتبة كثيرًا مؤخرًا لتقليل التكرار في مكتبة مساعدة عملاء IRC js.إنها أشياء رائعة - تساعد حقًا في تنظيف التعليمات البرمجية وتبسيطها.

بالإضافة إلى ذلك، إذا أصبح الأداء مشكلة (ولكن هذا lib خفيف جدًا)، فمن السهل إعادة الكتابة باستخدام وظيفة أصلية.

يمكنك استخدام الربط الأصلي للحصول على حل سريع من سطر واحد

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 noob، قادم من عالم بايثون.حتى هناك، الكاري لا يتم استخدامه كثيرًا، لكنه يمكن أن يكون مفيدًا في بعض الأحيان.لذلك قمت بتكوين وظيفة الكاري - انظر الروابط)

أولاً، سأبدأ بمكالمة أجاكس.لدي بعض المعالجة المحددة التي يجب القيام بها عند النجاح، ولكن في حالة الفشل، أريد فقط أن أقدم للمستخدم التعليقات التي تتطلب ذلك شئ ما أسفرت بعض الخطأ.في الكود الفعلي الخاص بي، أعرض تعليقات الأخطاء في لوحة التشغيل، ولكنني أستخدم التسجيل هنا فقط.

لقد قمت بتعديل عنوان 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