تركيب الدوال (علوم الحاسوب)
في علم الحاسوب ، يُعدّ تركيب الدوال عملية أو آلية لدمج الدوال البسيطة لبناء دوال أكثر تعقيدًا. وكما هو الحال في تركيب الدوال في الرياضيات ، تُمرّر نتيجة كل دالة كمعامل للدالة التالية، وتكون نتيجة الدالة الأخيرة هي نتيجة العملية الكلية.
كثيرًا ما يستخدم المبرمجون الدوال لنتائج دوال أخرى، وتسمح بذلك معظم لغات البرمجة. في بعض الحالات، يكون تركيب الدوال مفيدًا كدالة مستقلة تُستخدم لاحقًا. يمكن دائمًا تعريف مثل هذه الدالة، لكن اللغات التي تدعم الدوال من الدرجة الأولى تُسهّل هذه العملية.
إن القدرة على تركيب الدوال بسهولة تشجع على تجزئة الدوال (تقسيمها) لتحسين سهولة الصيانة وإعادة استخدام الكود . وبشكل عام، يمكن بناء الأنظمة الكبيرة من خلال تركيب برامج كاملة.
بالمعنى الدقيق، ينطبق تركيب الدوال على الدوال التي تعمل على كمية محدودة من البيانات، حيث تعالج كل خطوة منها البيانات بالتتابع قبل تمريرها إلى الخطوة التالية. أما الدوال التي تعمل على بيانات غير محدودة (مثل تدفق البيانات أو غيرها من البيانات المشفرة ) فتُعرف باسم المرشحات ، وهي متصلة في مسار معالجة ، وهو ما يُشابه تركيب الدوال ويمكن تنفيذها بشكل متزامن .
استدعاءات دالة التأليف
على سبيل المثال، لنفترض أن لدينا دالتين f و g ، كما في z = f ( y ) و y = g ( x ) . تركيبهما يعني أننا نحسب أولاً y = g ( x ) ، ثم نستخدم y لحساب z = f ( y ) . إليك المثال بلغة C :
float x , y , z ; // ... y = g ( x ); z = f ( y );يمكن دمج الخطوات إذا لم نُعطِ اسمًا للنتيجة الوسيطة:
z = f ( g ( x ));على الرغم من اختلاف طولهما، فإن هاتين الطريقتين تُنتجان النتيجة نفسها. تتطلب الطريقة الثانية سطرًا واحدًا فقط من التعليمات البرمجية، ويُشار إليها عادةً باسم "الطريقة المركبة للغاية". تُعد سهولة القراءة، وبالتالي سهولة الصيانة، إحدى مزايا الطرق المركبة للغاية، نظرًا لأنها تتطلب عددًا أقل من أسطر التعليمات البرمجية، مما يُقلل من "مساحة البرنامج". [ 1 ] وقد أثبت ديماركو وليستر تجريبيًا وجود علاقة عكسية بين مساحة البرنامج وسهولة صيانته. [ 2 ] من ناحية أخرى، قد يكون من الممكن الإفراط في استخدام الطرق المركبة للغاية. إذ قد يؤدي تداخل عدد كبير جدًا من الدوال إلى نتيجة عكسية، مما يجعل التعليمات البرمجية أقل قابلية للصيانة.
في لغة تعتمد على بنية المكدس ، يصبح التركيب الوظيفي أكثر طبيعية: إذ يتم تنفيذه عن طريق الربط ، وهو عادةً الأسلوب الأساسي لتصميم البرامج. المثال أعلاه مكتوب بلغة فورث :
صديقتي
سيأخذ هذا ما كان موجودًا في المكدس سابقًا، ويطبق عليه الدالة g، ثم الدالة f، ويترك النتيجة في المكدس. انظر إلى تدوين التركيب اللاحق للاطلاع على التدوين الرياضي المقابل.
تسمية تركيب الدوال
لنفترض الآن أن الجمع بين استدعاء f() على نتيجة g() مفيد بشكل متكرر، والذي نريد تسميته foo() ليتم استخدامه كدالة في حد ذاته.
في معظم لغات البرمجة، يمكننا تعريف دالة جديدة يتم تنفيذها باستخدام التركيب. مثال في لغة C :
float foo ( float x ) { return f ( g ( x )); }(الصيغة المطولة مع الوسائط ستفي بالغرض أيضاً.) مثال بلغة فورث :
foo gf ;
في لغات مثل لغة C ، الطريقة الوحيدة لإنشاء دالة جديدة هي تعريفها في مصدر البرنامج، مما يعني أنه لا يمكن تركيب الدوال أثناء التشغيل . مع ذلك، يُمكن تقييم أي تركيبة من الدوال المُعرَّفة مسبقًا.
#include <stdio.h>typedef int FXN ( int );int f ( int x ) { return x + 1 ; } int g ( int x ) { return x * 2 ; } int h ( int x ) { return x - 3 ; }int eval ( FXN * fs [], int size , int x ) { for ( int i = 0 ; i < size ; i ++ ) x = ( * fs [ i ])( x );أعد x ؛ }int main () { // ((6 + 1) * 2) - 3 = 11 FXN * arr [] = { f , g , h }; printf ( "%d \n " , eval ( arr , 3 , 6 ));// ((6 - 3) * 2) + 1 = 7 arr [ 2 ] = f ; arr [ 0 ] = h ; printf ( "%d \n " , eval ( arr , 3 , 6 )); }تأليف من الدرجة الأولى
في لغات البرمجة الوظيفية ، يمكن التعبير عن تركيب الدوال بشكل طبيعي باستخدام دالة أو عامل من الرتبة العليا . أما في لغات البرمجة الأخرى، فيمكنك كتابة آلياتك الخاصة لتنفيذ تركيب الدوال.
هاسكل
في لغة هاسكل ، يصبح المثال foo = f ∘ g المذكور أعلاه كالتالي:
foo = f . g
باستخدام عامل التركيب المدمج (.) والذي يمكن قراءته على أنه f بعد g أو g مركبة مع f .
يمكن تعريف عامل التركيب ∘ نفسه في لغة هاسكل باستخدام تعبير لامدا :
( . ) :: ( b -> c ) -> ( a -> b ) -> a -> c f . g = \ x -> f ( g x )يصف السطر الأول نوع (.) - فهو يأخذ زوجًا من الدوال، f و g ، ويعيد دالة (تعبير لامدا في السطر الثاني). لاحظ أن لغة هاسكل لا تتطلب تحديد أنواع المدخلات والمخرجات الدقيقة لـ f و g؛ فالرموز a و b و c و x هي عناصر نائبة؛ العلاقة بين f و g هي المهمة فقط (يجب أن تقبل f ما تعيده g). هذا يجعل (.) عاملًا متعدد الأشكال .
عامل متعدد الأشكال ذو وسائط متعددة، في تدوين هاسكل الخالي من النقاط
f g x yهو [ 3 ]
( . ) . ( . )حيث لا تُعدّ أسماء المعاملات مهمة، [ 3 ] كما ذُكر أعلاه. يُنسب الفضل إلى ميخال شيفشيك في تقديم شرح منهجي لتركيب الدوال باستخدام معاملات إضافية إلى سي إس كالفاني. [ 4 ]
التلعثم
تُعدّ متغيرات لغة Lisp ، وخاصة Scheme ، وإمكانية تبادل التعليمات البرمجية والبيانات جنبًا إلى جنب مع معالجة الدوال، مناسبة للغاية للتعريف المتكرر للمشغل التركيبي المتغير .
( define ( compose.fs ) ( if ( null? fs ) ( lambda ( x ) x ) ; إذا لم يتم إعطاء أي وسيط، فسيتم تقييمها إلى دالة التطابق ( lambda ( x ) (( car fs ) (( apply compose ( cdr fs ) ) x ))))); أمثلة ( تعريف ( إضافة علامة تعجب str ) ( إلحاق سلسلة نصية str "!" ))( define givebang ( compose string->symbol add-a-bang symbol->string ))( givebang 'set ) ; ===> set!; تركيب مجهول (( تركيب جذر تربيعي - تربيع ) 5 ) ; ===> 0+5iAPL
تتضمن العديد من لهجات لغة APL خاصية تركيب الدوال المدمجة باستخدام الرمز ∘. تعمل هذه الدالة ذات الرتبة الأعلى على توسيع نطاق تركيب الدوال ليشمل التطبيق الثنائي للدالة الموجودة على الجانب الأيسر بحيث A f∘g Bيكون A f g B.
foo ← f ∘ gبالإضافة إلى ذلك، يمكنك تحديد تركيب الدوال:
o ← { ⍺⍺ ⍵⍵ ⍵ }في اللهجة التي لا تدعم التعريف المضمن باستخدام الأقواس، يتوفر التعريف التقليدي:
∇ r ← ( f o g ) x r ← f g x ∇راكو
يحتوي Raku مثل Haskell على عامل تركيب الدوال المدمج، والفرق الرئيسي هو أنه يُكتب على النحو التالي: ∘أو o.
my & foo = & f ∘ & g ;كما هو الحال في لغة هاسكل، يمكنك تعريف المعامل بنفسك. في الواقع، إليك كود راكو المستخدم لتعريفه في تطبيق راكودو .
# يختلف التنفيذ قليلاً هنا لأنه يتحايل على صيغة proto sub infix :<∘> (&?, &?) is equiv(&[~]) is assoc<left> { * }multi sub infix :<∘> () { *. self } # يسمح لـ `[∘] @array` بالعمل عندما تكون `@array` فارغة multi sub infix :<∘> (&f) { & f } # يسمح لـ `[∘] @array` بالعمل عندما تحتوي `@array` على عنصر واحد multi sub infix :<∘> (&f, &g --> Block) { ( & f ) . count > 1 ?? -> | args { f | g | args } !! -> | args { f g | args } }# قم بتعريفه على أنه تهجئة "تكساس" (كل شيء أكبر، و ASCII في تكساس) بادئة & الخاصة بي : <o> : = بادئة & : <∘> ;نيم
تدعم لغة Nim صيغة استدعاء الدوال الموحدة ، مما يسمح بتركيب الدوال بشكل عشوائي من خلال عامل صيغة الطريقة .. [ 5 ]
دالة foo ( a : عدد صحيح ): سلسلة نصية = $ a دالة bar ( a : سلسلة نصية ، count : عدد صحيح ): seq [ سلسلة نصية ] = for i in 0 .. < count : result.add ( a ) دالة baz ( a : seq [ سلسلة نصية ] ) = for i in a : echo i# مكافئ! echo foo ( 5 ) .bar ( 6 ) .baz () echo baz ( bar ( 6 , foo ( 5 )))بايثون
في لغة بايثون ، تتمثل إحدى طرق تعريف التركيب لأي مجموعة من الدوال في استخدام دالة functools.reduce :
من functools استورد reduce ومن typing استورد Callabledef compose ( * funcs ) -> Callable [[ int ], int ]: """تُركّب مجموعة من الدوال (f(g(h(...)))) في دالة مركبة واحدة.""" return reduce ( lambda f , g : lambda x : f ( g ( x )), funcs )# مثال : f = λx : x + 1 ، g = λx : x * 2 ، h = λx : x - 3# استدعاء الدالة x=10 : ((x - 3) * 2) + 1 = 15 print ( compose ( f , g , h )( 10 ))جافا سكريبت
في لغة جافا سكريبت، يمكننا تعريفها كدالة تأخذ دالتين f و g ، وتنتج دالة:
دالة o ( f , g ) { return دالة ( x ) { return f ( g ( x )); } }// بدلاً من ذلك، باستخدام عامل الإرجاع وتعبيرات لامدا في ES2015: const compose = (... fs ) => ( x ) => fs . reduceRight (( acc , f ) => f ( acc ), x )سي شارب
في لغة C#، يمكننا تعريفها كطريقة امتداد تأخذ الدوال f و g ، وتنتج دالة جديدة :
// مثال على الاستدعاء: // var c = f.ComposeWith(g); // // Func<int, bool> g = _ => ... // Func<bool, string> f = _ => ...public static Func < T1 , T3 > ComposeWith < T1 , T2 , T3 > ( this Func < T2 , T3 > f , Func < T1 , T2 > g ) => x => f ( g ( x ));روبي
تتيح لك لغات مثل روبي إنشاء عامل ثنائي بنفسك:
class Proc def compose ( other_fn ) -> ( * as ) { other_fn.call ( call ( * as ) ) } end alias_method : + , :compose endf = -> ( x ) { x * 2 } g = -> ( x ) { x ** 3 } ( f + g ) .call ( 12 ) # = > 13824ومع ذلك، تم تقديم عامل تركيب الدوال الأصلي في Ruby 2.6: [ 6 ]
f = proc { | x | x + 2 } g = proc { | x | x * 3 } ( f << g ) . call ( 3 ) # -> 11; مطابقة لـ f(g(3)) ( f >> g ) . call ( 3 ) # -> 15; مطابقة لـ g(f(3))دراسة بحثية
إن مفاهيم التأليف، بما في ذلك مبدأي التأليف والتركيب ، منتشرة على نطاق واسع لدرجة أنها أدت إلى ظهور العديد من فروع البحث بشكل منفصل. وفيما يلي عينة من أنواع الأبحاث التي يحتل فيها مفهوم التأليف مكانة مركزية.
- قام ستيل (1994) بتطبيق تركيب الدوال بشكل مباشر على تجميع لبنات البناء المعروفة باسم " monads " في لغة البرمجة Haskell .
- تناول ماير (1988) مشكلة إعادة استخدام البرمجيات من حيث قابلية التركيب.
- قام عبادي ولامبورت (1993) بتعريف قاعدة إثبات رسمية للتركيب الوظيفي تضمن سلامة البرنامج وحيويته.
- حدد كراخت (2001) شكلاً معززاً من التركيبية من خلال وضعها في نظام سيميائي وتطبيقها على مشكلة الغموض الهيكلي الذي يصادف بشكل متكرر في اللغويات الحاسوبية .
- قام فان جيلدر وبورت (1993) بدراسة دور التركيبية في الجوانب التناظرية لمعالجة اللغة الطبيعية.
- وفقًا لمراجعة أجراها جيبونز (2002) ، فإن المعالجة الرسمية للتكوين تكمن وراء التحقق من صحة تجميع المكونات في لغات البرمجة المرئية مثل Visual Age من IBM للغة Java .
التركيب واسع النطاق
يمكن التعامل مع البرامج أو الأنظمة بأكملها كدوال، والتي يمكن تركيبها بسهولة إذا كانت مدخلاتها ومخرجاتها محددة جيدًا. [ 7 ] وقد حققت خطوط الأنابيب التي تسمح بالتركيب السهل للمرشحات نجاحًا كبيرًا لدرجة أنها أصبحت نمطًا تصميميًا لأنظمة التشغيل.
تُخالف الإجراءات الإجرائية ذات الآثار الجانبية مبدأ الشفافية المرجعية ، وبالتالي لا يمكن تركيبها بسهولة. مع ذلك، إذا اعتبرنا "حالة العالم" قبل وبعد تنفيذ الكود بمثابة مدخلات ومخرجات، نحصل على دالة سليمة. يتوافق تركيب هذه الدوال مع تنفيذ الإجراءات واحدًا تلو الآخر. يستخدم نموذج الموناد هذه الفكرة لدمج الآثار الجانبية والمدخلات/المخرجات في اللغات الوظيفية.
انظر أيضاً
ملحوظات
- ↑ كوكس (1986) ، الصفحات 15-17
- ↑ DeMarco & Lister (1995) ، ص 133-135.
- 1 2 ميشال سيفتشيك https://melkornemesis.medium.com/haskell-explained-3f91658a67d3 (10 يناير 2021) هاسكل: (.) . (.) وأوضح
- ↑ تشارلز سكالفاني https://gist.github.com/cscalfani/30ff149a75fc5580d1f8aec61f8e5283 (2017) التركيب الوظيفي مع معلمات متعددة في هاسكل
- ↑ "دليل لغة نيم: صيغة استدعاء الدوال" . nim-lang.org . تم الاطلاع عليه بتاريخ 17-08-2023 .
- ↑ "إصدار روبي 2.6.0" . www.ruby-lang.org . تاريخ الاسترجاع: 4 يناير 2019 .
- ↑ ريموند (2003)
مراجع
- عبادي، مارتن ؛ لامبورت، ليزلي (1993)، "تأليف المواصفات" (ملف PDF) ، معاملات ACM في لغات البرمجة والأنظمة ، 15 (1): 73-132 ، doi : 10.1145/151646.151649.
- كوكس، براد (1986)، البرمجة الكائنية التوجه: منهج تطوري ، ريدينغ، ماساتشوستس: أديسون-ويسلي، رمز Bibcode : 1986oopa.book.....C ، رقم ISBN 978-0-201-54834-1.
- داوم، هال 3، درس تعليمي آخر في لغة هاسكل.
- ديماركو، توم ؛ ليستر، تيم (1995)، "تطوير البرمجيات: أحدث التقنيات مقابل أحدث الممارسات"، في ديماركو، توم (محرر)، لماذا تكلف البرمجيات الكثير، وألغاز أخرى من عصر المعلومات ، نيويورك، نيويورك: دار دورست هاوس، ISBN 0-932633-34-X.
- فان جيلدر، تيموثي ؛ بورت، روبرت (1993)، "ما وراء الرمزي: مقدمة إلى كاماسوترا التركيبية"، في هونافار، فاسانت ؛ أور، ليونارد (محرران)، معالجة الرموز والنماذج الترابطية في الذكاء الاصطناعي والإدراك: خطوات نحو التكامل ، دار النشر الأكاديمية.
- جيبونز، جيريمي (2002)، "نحو دلالات قائمة على حدود التنسيق للبرمجة المرئية"، في أرباب، فرهاد؛ تالكوت، كارولين (محرران)، وقائع المؤتمر الدولي الخامس حول نماذج ولغات التنسيق (ملف PDF) ، سلسلة محاضرات في علوم الحاسوب، المجلد 2315، سبرينغر-فيرلاغ، الصفحات 339-350 ، doi : 10.1007/3-540-46000-4_18 ، ISBN 978-3-540-43410-8.
- كورن، هنري؛ ليبري، ألبرت (1974)، مدخل تمهيدي للدوال ، نيويورك، نيويورك: ماكجرو هيل، ISBN 0-07-035339-5.
- كراخت، ماركوس (2001)، "التركيبية الصارمة وقواعد الحركة الحرفية"، وقائع المؤتمر الدولي الثالث حول الجوانب المنطقية للغويات الحاسوبية ، سلسلة محاضرات في علوم الحاسوب، المجلد 2014، دار نشر سبرينغر، الصفحات 126-143 ، doi : 10.1007/3-540-45738-0_8 ، ISBN 978-3-540-42251-8.
- ماير، برتراند (1988)، بناء البرمجيات الموجهة للكائنات ، نيويورك، نيويورك: برنتيس هول، ص 13-15 ، ISBN 0-13-629049-3.
- ميلر، جورج أ. (1956)، "الرقم السحري سبعة، زائد أو ناقص اثنين: بعض القيود على قدرتنا على معالجة المعلومات" ، مجلة علم النفس ، 63 (2): 81-97 ، doi : 10.1037/h0043158 ، hdl : 11858/00-001M-0000-002C-4646-B ، PMID 13310704 ، مؤرشف من الأصل في 19 يونيو 2010 ، تم استرجاعه في 2 مايو 2010 .
- بيرس، بنجامين سي؛ تيرنر، ديفيد ن. (2000)، "بيكت: لغة برمجة مبنية على حساب باي"، البرهان، اللغة، والتفاعل: مقالات تكريمًا لروبن ميلنر ، سلسلة أسس الحوسبة، كامبريدج، ماساتشوستس: مطبعة معهد ماساتشوستس للتكنولوجيا، الصفحات 455-494 ، ISBN 0-262-16188-5.
- ريموند، إريك س. (2003)، "1.6.3 قاعدة التركيب: تصميم البرامج بحيث تكون متصلة ببرامج أخرى" ، فن برمجة يونكس ، أديسون-ويسلي، ص 15-16 ، ISBN 978-0-13-142901-7.
- ستيل، جاي إل. الابن (1994)، "بناء المفسرات من خلال تركيب المونادات" ، وقائع ندوة ACM الحادية والعشرين حول مبادئ لغات البرمجة ، الصفحات 472-492 ، doi : 10.1145/174675.178068 ، ISBN 0-89791-636-0.
- مواضيع لغات البرمجة
- الدوال ذات الرتبة العليا
