سؤال

رأيت إشارات إلى بالكاري وظائف في عدة مقالات ومدونات ولكن لا أستطيع أن أجد تفسيرا جيدا (أو على الأقل واحدة من المنطقي!)

هل كانت مفيدة؟

المحلول

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

function add (a, b) {
  return a + b;
}

add(3, 4); // returns 7

هذه هي وظيفة أن يأخذ اثنين من الحجج ، و ب ، و إرجاع المبلغ.ونحن الآن الكاري هذه الوظيفة:

function add (a) {
  return function (b) {
    return a + b;
  }
}

هذه هي وظيفة أن يأخذ وسيطة واحدة ، ، وإرجاع وظيفة أن يأخذ حجة أخرى ، ب ، و أن الدالة بإرجاع المبلغ.

add(3)(4);

var add3 = add(3);

add3(4);

البيان الأول عوائد 7 ، مثل إضافة(3, 4) بيان.البيان الثاني يحدد وظيفة جديدة تسمى add3 أنه سيتم إضافة 3 إلى الحجة.هذا هو ما بعض الناس قد يسمون الإغلاق.البيان الثالث يستخدم add3 العملية إلى إضافة 3 إلى 4 مرة أخرى إنتاج 7 نتيجة لذلك.

نصائح أخرى

في الجبر من الوظائف ، التعامل مع الوظائف التي تأخذ حجج متعددة (أو ما يعادلها حجة واحدة هي N-tuple) هو غير لائق إلى حد ما -- ولكن ، كما موسى Schönfinkel (مستقل, هاسكل كاري) ثبت انه لا حاجة:كل ما تحتاجه من وظائف التي تأخذ حجة واحدة.

إذا كيف يمكنك التعامل مع شيء سيكون بطبيعة الحال صريحة كما يقولون ، f(x,y)?حسنا, يمكنك أن يعادل f(x)(y) -- f(x), اتصل عليه g, وظيفة, و يمكنك تطبيق هذه الدالة y.وبعبارة أخرى, لديك فقط الوظائف التي تأخذ حجة واحدة -- ولكن بعض هذه الدالات بإرجاع الوظائف الأخرى (التي تأخذ أيضا حجة واحدة;-).

كالعادة ، ويكيبيديا لديه لطيفة ملخص حول دخول هذا مع العديد من المؤشرات المفيدة (ربما بما في ذلك تلك المتعلقة المفضلة لديك اللغات;-) وكذلك أكثر قليلا رياضية صارمة العلاج.

هنا مثالا ملموسا:

افترض أن لديك وظيفة تقوم بحساب قوة الجاذبية المؤثرة على جسم ما.إذا كنت لا تعرف الصيغة يمكنك العثور عليها هنا.هذه الدالة تأخذ في ثلاثة المعلمات الضرورية كما الحجج.

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

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

على سبيل المثال ، في F# يمكنك تحديد وظيفة على النحو التالي:-

let f x y z = x + y + z

هنا وظيفة و يأخذ المعلمات x ، y و z و مبالغ لهم معا في ذلك:-

f 1 2 3

عوائد 6.

من التعريف يمكننا بالتالي تحديد الكاري وظيفة f:-

let curry f = fun x -> f x

حيث 'متعة x -> f x' هو امدا وظيفة equivilent أن x => f(x) في C#.هذه المعطيات وظيفة كنت ترغب في الكاري و يعود وظيفة يأخذ حجة واحدة وإرجاع المحدد وظيفة مع أول حجة تعيين المدخلات الوسيطة.

باستخدام المثال السابق يمكننا الحصول على الكاري و بالتالي:-

let curryf = curry f

ثم يمكننا القيام بما يلي:-

let f1 = curryf 1

الذي يقدم لنا وظيفة f1 وهو equivilent f1 y z = 1 + y + z.هذا يعني أننا يمكن أن تفعل ما يلي:-

f1 2 3

والتي ترجع 6.

هذه العملية غالبا ما يتم الخلط 'جزئية وظيفة التطبيق' والتي يمكن تعريفها على النحو التالي:-

let papply f x = f x

على الرغم من أننا يمكن أن تمتد إلى أكثر من معلمة واحدة ، أي:-

let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.

تطبيق جزئي سوف تأخذ وظيفة المعلمة(s) والعودة وظيفة تتطلب واحد أو أكثر أقل المعلمات ، كما في المثالين السابقين تظهر تنفذ مباشرة في مستوى F# تعريف الدالة حتى نتمكن من تحقيق النتيجة السابقة بالتالي:-

let f1 = f 1
f1 2 3

الذي سيعود نتيجة 6.

في الختام:-

الفرق بين التمشيط جزئية وظيفة التطبيق هو أن:-

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

let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6

جزئية وظيفة التطبيق هو أكثر مباشرة - يلزم وظيفة واحدة أو أكثر الحجج و إرجاع وظيفة مع أول ن الحجج تعيين n الحجج المحدد.على سبيل المثال:-

let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6

يمكن أن يكون وسيلة إلى استخدام وظائف لجعل الوظائف الأخرى.

في جافا سكريبت:

let add = function(x){
  return function(y){ 
   return x + y
  };
};

من شأنها أن تسمح لنا أن نسميها هكذا:

let addTen = add(10);

عندما يدير 10 يتم تمريرها في x;

let add = function(10){
  return function(y){
    return 10 + y 
  };
};

وهو ما يعني أننا عادت هذه الوظيفة:

function(y) { return 10 + y };

حتى عند استدعاء

 addTen();

كنت حقا الدعوة:

 function(y) { return 10 + y };

حتى إذا كنت تفعل هذا:

 addTen(4)

انها نفس:

function(4) { return 10 + 4} // 14

لذلك لدينا addTen() دائما يضيف عشرة إلى ما نحن نمر في.ونحن يمكن أن تجعل وظائف مماثلة في نفس الطريق:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...

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

هنا هو لعبة سبيل المثال في بايثون:

>>> from functools import partial as curry

>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
        print who, 'said regarding', subject + ':'
        print '"' + quote + '"'


>>> display_quote("hoohoo", "functional languages",
           "I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."

>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")

>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."

(فقط باستخدام سلسلة عبر + لتجنب الهاء من غير بيثون المبرمجين.)

تحرير لإضافة:

انظر http://docs.python.org/library/functools.html?highlight=partial#functools.partial, مما يدل أيضا على الجزئية الكائن مقابلوظيفة التمييز في طريقة الثعبان ينفذ هذا.

إذا كنت تفهم partial كنت في منتصف الطريق هناك.فكرة partial هو preapply الحجج إلى وظيفة والعطاء وظيفة جديدة التي يريد سوى ما تبقى من الحجج.عند هذه وظيفة جديدة تسمى ويشمل مسبقة الحجج جنبا إلى جنب مع مهما كانت الحجج المقدمة إليه.

في Clojure + هي وظيفة ولكن لجعل الأمور بشكل واضح:

(defn add [a b] (+ a b))

قد تكون على علم أن inc وظيفة ببساطة يضيف 1 إلى أي رقم مرت.

(inc 7) # => 8

دعونا نبني أنفسنا باستخدام partial:

(def inc (partial add 1))

هنا نعود آخر وظيفة له 1 تحميلها في الحجة الأولى من add.كما add يأخذ اثنين من الحجج الجديدة inc وظيفة يريد فقط b حجة-لا 2 الحجج كما كان من قبل منذ 1 وقد تم بالفعل جزئيا تطبيقها.وهكذا partial هو أداة يمكن من خلالها خلق وظائف جديدة مع القيم الافتراضية presupplied.هذا هو السبب في لغة وظيفية وظائف في كثير من الأحيان ترتيب الحجج من العام إلى الخاص.هذا يجعل من السهل إعادة استخدام هذه الوظائف يمكن من خلالها بناء وظائف أخرى.

الآن تخيل لو أن اللغة كانت ذكية بما فيه الكفاية لفهم introspectively أن add أراد حجتين.عندما مرت علينا بل حجة واحدة بدلا من ماضيتان ما إذا كانت وظيفة جزئيا طبق الحجة مرت علينا نيابة عنا فهم أننا ربما من المفترض أن توفر حجة أخرى لاحقا ؟ نحن يمكن بعد تحديد inc صراحة دون استخدام partial.

(def inc (add 1)) #partial is implied

هذا هو الطريق بعض اللغات التصرف.فإنه مفيد بشكل استثنائي عندما يريد أحد أن يؤلف وظائف في أكبر التحولات.هذا من شأنه أن يؤدي أحد محولات الطاقة.

لقد وجدت هذا المقال و المقال مراجع مفيدة من أجل فهم أفضل التمشيط:http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

مثل غيرهم ذكره ، هو مجرد وسيلة واحدة المعلمة وظيفة.

هذا هو مفيدة في هذا لا يجب أن نفترض كيف العديد من المعلمات سيتم تمريرها في, لذلك أنت لا تحتاج إلى 2 المعلمة المعلمة 3 و 4 المعلمة وظائف.

الكاري يمكن تبسيط التعليمات البرمجية الخاصة بك.هذا هو واحد من الأسباب الرئيسية لاستخدام هذا.التمشيط هو عملية تحويل وظيفة يقبل n الحجج في ن الوظائف التي تقبل إلا حجة واحدة.

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

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

على سبيل المثال:

function curryMinus(x) 
{
  return function(y) 
  {
    return x - y;
  }
}

var minus5 = curryMinus(1);
minus5(3);
minus5(5);

أنا أيضا أستطيع أن أفعل...

var minus7 = curryMinus(7);
minus7(3);
minus7(5);

هذا هو كبير جدا مما يجعل التعليمات البرمجية المعقدة أنيق و التعامل مع غير المتزامنة الطرق وما إلى ذلك.

التمشيط هو ترجمة وظيفة من للاستدعاء كما f(a, b, c) في للاستدعاء كما f(a)(b)(c).

وإلا التمشيط عند كسر الوظيفة التي تستغرق عدة حجج في سلسلة من الوظائف التي تأخذ جزءا من الحجج.

حرفيا ، التمشيط هو التحول من الوظائف:من طريقة واحدة من يدعو إلى آخر.في جافا سكريبت, نحن عادة ما جعل المجمع للحفاظ على وظيفتها الأصلية.

التمشيط لا استدعاء دالة.فقط يحول ذلك.

دعونا جعل الكاري الوظيفة التي يقوم التمشيط لمدة سنتين حجة وظائف.وبعبارة أخرى ، curry(f) لمدة سنتين-حجة f(a, b) ويترجم ذلك في f(a)(b)

function curry(f) { // curry(f) does the currying transform
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

// usage
function sum(a, b) {
  return a + b;
}

let carriedSum = curry(sum);

alert( carriedSum(1)(2) ); // 3

كما يمكنك أن ترى تنفيذ سلسلة من الأغلفة.

  • نتيجة curry(func) هو المجمع function(a).
  • عندما يتم استدعاء مثل sum(1), فإن الحجة هي المحفوظة في المعجمية بيئة جديدة المجمع هو عاد function(b).
  • ثم sum(1)(2) أخيرا المكالمات function(b) توفير 2 ، وأنه يمر الدعوة إلى الأصل متعددة حجة المبلغ.

أ بالكاري يتم تطبيق وظيفة متعددة حجة قوائم بدلا من مجرد واحد.

هنا هو العادية, غير بالكاري وظيفة ، والذي يضيف اثنين من الباحث المعلمات ، x و y:

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3

هنا هي وظيفة مماثلة هذا بالكاري.بدلا من ذلك من قائمة واحدة من اثنين الباحث المعلمات تطبيق هذه الوظيفة إلى قائمتين من أحد المعلمة الباحث بعضها:

scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3

ما يحدث هنا هو أنه عند استدعاء curriedSum, كنت في الواقع الحصول على اثنين من الوظيفة التقليدية الدعاء العودة إلى الوراء.أول وظيفة الاحتجاج يأخذ واحد المعلمة الباحث اسمه x و يعود وظيفة قيمة الوظيفة الثانية.هذه الوظيفة الثانية يأخذ الباحث المعلمة y.

وهنا وظيفة اسمه first لا في الروح ما أول التقليدية وظيفة الاحتجاج curriedSum أن تفعل:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

تطبيق 1 إلى الوظيفة الأولى—وبعبارة أخرى ، استدعاء الدالة الأولى ويمر في 1 —ينتج الوظيفة الثانية:

scala> val second = first(1)
second: (Int) => Int = <function1>

تطبيق 2 الثانية وظيفة ينتج النتيجة:

scala> second(2)
res6: Int = 3

مثال التمشيط سيكون عند وجود وظائف تعرف فقط إحدى المعلمات في هذه اللحظة:

على سبيل المثال:

func aFunction(str: String) {
    let callback = callback(str) // signature now is `NSData -> ()`
    performAsyncRequest(callback)
}

func callback(str: String, data: NSData) {
    // Callback code
}

func performAsyncRequest(callback: NSData -> ()) {
    // Async code that will call callback with NSData as parameter
}

هنا, منذ كنت لا تعرف المعلمة الثانية على الاستدعاء عند إرسال رسالة إلى performAsyncRequest(_:) سيكون لديك لخلق آخر لامدا / إغلاق لإرسال هذا إلى وظيفة.

جميع إجابات أخرى التمشيط يساعد على خلق مطبق جزئيا الوظائف.جافا سكريبت لا توفر دعم أصلي التلقائي التمشيط.حتى الأمثلة الواردة أعلاه قد لا تساعد في عملية الترميز.هناك بعض مثال ممتاز في livescript (وهو أساسا برمجيا إلى js) http://livescript.net/

times = (x, y) --> x * y
times 2, 3       #=> 6 (normal use works as expected)
double = times 2
double 5         #=> 10

في المثال أعلاه عندما كنت قد أعطيت أقل من الحجج livescript يولد جديدة بالكاري وظيفة بالنسبة لك (مزدوجة)

هنا يمكنك أن تجد تفسير بسيط من التمشيط التنفيذ في C#.في التعليقات ، لقد حاولت أن أبين كيف التمشيط يمكن أن تكون مفيدة:

public static class FuncExtensions {
    public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        return x1 => x2 => func(x1, x2);
    }
}

//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);

//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times 
//with different input parameters.

int result = func(1);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top