نواصل سلسلة مقالاتنا حول البرمجة الوظيفية في F #. نتحدث اليوم عن المساعدة وتكوين الوظائف ، بالإضافة إلى مقارنة التكوين وخط الأنابيب. انظر تحت القط!

المساعدة وتكوين الوظائف
الوظيفة المساعدة
افترض أن هناك سلسلة من الوظائف مكتوبة على التوالي. وبأي ترتيب سيتم دمجها؟
على سبيل المثال ، ماذا تعني هذه الوظيفة؟
let F xyz = xyz
هل يعني ذلك أنه يجب تطبيق الدالة y
على الوسيطة z
، ثم يجب تمرير النتيجة إلى x
؟ أي:
let F xyz = x (yz)
أو هل يتم تطبيق الدالة x
على الوسيطة y
، وبعد ذلك سيتم تقييم الدالة التي تم الحصول عليها نتيجة للوسيطة z
؟ أي:
let F xyz = (xy) z
- الخيار الثاني صحيح.
- وقد ترك استخدام الوظائف المساعدة.
xyz
تعني نفس (xy) z
.- و
wxyz
يساوي ((wx) y) z
. - هذا لا ينبغي أن يبدو رائعا.
- لقد رأينا بالفعل كيف يعمل التطبيق الجزئي.
- إذا تحدثنا عن
x
كدالة ذات معلمتين ، فإن (xy) z
هي نتيجة تطبيق جزئي للمعلمة الأولى ، يليها تمرير الوسيطة z
إلى الدالة المتوسطة.
إذا كنت بحاجة إلى المساعدة الاجتماعية المناسبة ، يمكنك استخدام الأقواس أو الأنابيب. الإدخالات الثلاثة التالية متكافئة:
let F xyz = x (yz) let F xyz = yz |> x // let F xyz = x <| yz //
كتمرين ، حاول عرض توقيعات هذه الوظائف دون حساب حقيقي.
تكوين الوظيفة
ذكرنا تكوين الوظائف عدة مرات ، ولكن ماذا يعني هذا المصطلح حقًا؟ يبدو الأمر مخيفًا للوهلة الأولى ، ولكن في الواقع ، كل شيء بسيط للغاية.
لنفترض أن لدينا دالة "f" تحدد النوع "T1" إلى النوع "T2". لدينا أيضًا دالة "g" التي تحول النوع "T2" إلى النوع "T3". ثم يمكننا توصيل ناتج "f" وإدخال "g" ، وإنشاء وظيفة جديدة تحول النوع "T1" إلى النوع "T3".

على سبيل المثال:
let f (x:int) = float x * 3.0 // f - int->float let g (x:float) = x > 4.0 // g - float->bool
يمكننا إنشاء دالة جديدة "h" تأخذ ناتج "f" وتستخدمها كمدخل لـ "g".
let h (x:int) = let y = f(x) g(y) // g
أكثر إحكاما قليلا:
let h (x:int) = g ( f(x) ) // h int->bool // h 1 h 2
حتى الآن ، بهذه البساطة. هذا مثير للاهتمام ، يمكننا تحديد وظيفة جديدة "تأليف" ، تأخذ الوظائف "f" و "g" وتجمعها دون معرفة توقيعاتها.
let compose fgx = g ( f(x) )
بعد التنفيذ ، يمكنك أن ترى أن المترجم قرر بشكل صحيح أن " f
" هي دالة للنوع العام 'a
للنوع العام 'b
، و ' g
' يقتصر على إدخال النوع 'b
:
val compose : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
(لاحظ أن التكوين العام للعمليات ممكن فقط لأن كل وظيفة لها معلمة إدخال واحدة وإخراج واحد. هذا الأسلوب غير ممكن في اللغات غير الوظيفية.)
كما نرى ، يتم استخدام هذا التعريف لعامل التشغيل " >>
".
let (>>) fgx = g ( f(x) )
بفضل هذا التعريف ، يمكن بناء وظائف جديدة على أساس الوظائف الموجودة باستخدام التكوين.
let add1 x = x + 1 let times2 x = x * 2 let add1Times2 x = (>>) add1 times2 x // add1Times2 3
التسجيل الصريح مرهق للغاية. ولكن يمكنك تسهيل استخدامه.
أولاً ، يمكنك التخلص من المعلمة x
، وسيعيد التكوين تطبيقًا جزئيًا.
let add1Times2 = (>>) add1 times2
ثانياً ، لأن >>
عامل تشغيل ثنائي ، يمكنك وضعه في المركز.
let add1Times2 = add1 >> times2
يؤدي استخدام التكوين إلى جعل الشفرة أنظف وأكثر وضوحًا.
let add1 x = x + 1 let times2 x = x * 2 // let add1Times2 x = times2(add1 x) // let add1Times2 = add1 >> times2
استخدام عامل التكوين في الممارسة
عامل التكوين (مثل جميع مشغلي infix) له أولوية أقل من الوظائف العادية. هذا يعني أن الدوال المستخدمة في التكوين يمكن أن يكون لها وسيطات بدون استخدام الأقواس.
على سبيل المثال ، إذا كانت الدالتان "add" و "times" تحتوي على معلمات ، فيمكن تمريرهما أثناء التكوين.
let add nx = x + n let times nx = x * n let add1Times2 = add 1 >> times 2 let add5Times3 = add 5 >> times 3 // add5Times3 1
طالما أن المدخلات والمخرجات المقابلة للوظائف متطابقة ، يمكن للوظائف استخدام أي قيم. على سبيل المثال ، ضع في اعتبارك التعليمات البرمجية التالية التي تقوم بتنفيذ دالة مرتين:
let twice f = f >> f // ('a -> 'a) -> ('a -> 'a)
لاحظ أن المحول البرمجي قد استنتج أن " f
" يقبل ويعيد القيم من نفس النوع.
الآن فكر في وظيفة " +
". كما رأينا سابقًا ، يكون الإدخال (int->int)
، ولكن الناتج في الواقع (int->int)
. وبالتالي ، يمكن استخدام " +
" في " twice
". لذلك يمكنك كتابة:
let add1 = (+) 1 // (int -> int) let add1Twice = twice add1 // (int -> int) // add1Twice 9
من ناحية أخرى ، لا يمكنك الكتابة:
let addThenMultiply = (+) >> (*)
لأن الإدخال "*" يجب أن يكون int
، وليس int->int
(وهو ناتج الجمع).
ولكن إذا قمت بتصحيح الوظيفة الأولى بحيث تعود فقط int
، int
كل شيء:
let add1ThenMultiply = (+) 1 >> (*) // (+) 1 (int -> int) 'int' // add1ThenMultiply 2 7
يمكن أيضًا تنفيذ التكوين بترتيب عكسي عن طريق " <<
" ، إذا لزم الأمر:
let times2Add1 = add 1 << times 2 times2Add1 3
يستخدم التكوين العكسي بشكل أساسي لجعل الشفرة تشبه الإنجليزية ("تشبه الإنجليزية"). على سبيل المثال:
let myList = [] myList |> List.isEmpty |> not // myList |> (not << List.isEmpty) //
التكوين مقابل ناقل
قد يتم الخلط بينك بسبب الاختلاف الصغير بين التكوين والناقل ، مثل قد تبدو متشابهة للغاية.
أولاً ، انظر إلى تعريف خط الأنابيب:
let (|>) xf = fx
كل هذا يسمح لك بوضع حجج الدوال قبلها ، وليس بعدها. هذا كل شيء. إذا كانت الوظيفة تحتوي على العديد من المعلمات ، فيجب أن يكون الإدخال هو المعلمة الأخيرة (في مجموعة المعلمات الحالية ، وليس على الإطلاق). مثال رأيناه سابقًا:
let doSomething xyz = x+y+z doSomething 1 2 3 // 3 |> doSomething 1 2 //
التركيبة ليست هي نفسها ولا يمكن أن تكون بديلاً للأنبوب. في المثال التالي ، حتى الرقم 3 ليس دالة ، لذلك لا يمكن تمرير "الإخراج" للقيام doSomething
:
3 >> doSomething 1 2 // // f >> g g(f(x)) : doSomething 1 2 ( 3(x) ) // 3 ! // error FS0001: This expression was expected to have type 'a->'b // but here has type int
يشكو المترجم من أن القيمة "3" يجب أن تكون من نوع 'a->'b
.
قارن هذا بتعريف التكوين ، الذي يأخذ 3 حجج ، حيث يجب أن تكون الأولين وظيفتين.
let (>>) fgx = g ( f(x) ) let add nx = x + n let times nx = x * n let add1Times2 = add 1 >> times 2
ستؤدي محاولات استخدام خط الأنابيب بدلاً من التركيب إلى خطأ في الترجمة. في المثال التالي ، " add 1
" هي الدالة (الجزئية) int->int
، والتي لا يمكن استخدامها كمعلمة ثانية لـ " times 2
".
let add1Times2 = add 1 |> times 2 // // x |> f f(x) : let add1Times2 = times 2 (add 1) // add1 'int' // error FS0001: Type mismatch. 'int -> int' does not match 'int'
يشكو المترجم من أن " int->int
times 2
" يجب أن يقبل المعلمة int->int
، أي تكون دالة (int->int)->'a
.
موارد إضافية
هناك العديد من البرامج التعليمية لـ F # ، بما في ذلك المواد لأولئك الذين يأتون مع تجربة C # أو Java. قد تكون الروابط التالية مفيدة عندما تتعمق في F #:
كما تم وصف عدة طرق أخرى لبدء تعلم F # .
أخيرًا ، مجتمع F # ودود للغاية للمبتدئين. هناك محادثة نشطة للغاية في Slack ، تدعمها F # Software Foundation ، مع غرف للمبتدئين يمكنك الانضمام إليها بحرية . نوصي بشدة أن تفعل ذلك!
لا تنس زيارة موقع المجتمع الناطق باللغة الروسية F # ! إذا كان لديك أي أسئلة حول تعلم لغة ، يسعدنا مناقشتها في غرف الدردشة:
حول مؤلفي الترجمة
ترجمه kleidemos
تم إجراء تغييرات الترجمة والتحرير من خلال جهود المجتمع الناطق باللغة الروسية لمطوري F # . نشكر أيضًا schvepsss و shwars لإعداد هذه المقالة للنشر.