مشكلة التعبير

مشكلة التعبير هي مشكلة في لغات البرمجة تتعلق بقابلية توسيع ونمطية تجريدات البيانات ذات الأنواع الثابتة. الهدف هو تعريف تجريد بيانات قابل للتوسيع في كلٍ من تمثيلاته وسلوكياته، بحيث يمكن إضافة تمثيلات وسلوكيات جديدة إليه دون إعادة ترجمة الشيفرة البرمجية الموجودة، مع الحفاظ على سلامة الأنواع الثابتة (مثلًا، عدم وجود تحويلات). يكشف تعريف المشكلة عن أوجه قصور في نماذج البرمجة ولغاتها . وقد صاغ هذا المصطلح فيليب وادلر، أحد مؤلفي لغة هاسكل .

تاريخ

صاغ فيليب وادلر التحدي وأطلق عليه اسم "مشكلة التعبير" [ 1 ] استجابةً لمناقشة مع فريق لغات البرمجة بجامعة رايس . كما استشهد بثلاثة مصادر حددت سياق تحديه:

لاحظ جون رينولدز هذه المشكلة لأول مرة عام 1975. [ 2 ] ناقش رينولدز شكلين من أشكال تجريد البيانات: الأنواع المعرفة من قبل المستخدم، والتي تُعرف الآن باسم أنواع البيانات المجردة (ADTs) (لا ينبغي الخلط بينها وبين أنواع البيانات الجبرية )، وهياكل البيانات الإجرائية، والتي تُفهم الآن على أنها شكل بدائي من الكائنات ذات طريقة واحدة فقط. جادل رينولدز بأنها تُكمل بعضها بعضًا، حيث يمكن توسيع الأنواع المعرفة من قبل المستخدم بسلوكيات جديدة، ويمكن توسيع هياكل البيانات الإجرائية بتمثيلات جديدة. كما ناقش أعمالًا ذات صلة تعود إلى عام 1967. بعد خمسة عشر عامًا، في عام 1990، طبق ويليام كوك [ 3 ] فكرة رينولدز في سياق الكائنات وأنواع البيانات المجردة، والتي شهدت نموًا كبيرًا. حدد كوك مصفوفة التمثيلات والسلوكيات الضمنية في تجريد البيانات، وناقش كيف تستند أنواع البيانات المجردة إلى محور السلوك، بينما تستند الكائنات إلى محور التمثيل. يقدم شرحًا مستفيضًا للأعمال المتعلقة بأنواع البيانات المجردة (ADTs) والكائنات ذات الصلة بالمشكلة. كما استعرض تطبيقات كلا النمطين، وناقش قابلية التوسع في كلا الاتجاهين، وأكد على أهمية الكتابة الثابتة. والأهم من ذلك، أنه ناقش حالات توفر مرونة أكبر مما أخذه رينولدز في الاعتبار، بما في ذلك استيعاب الدوال وتحسينها.

في مؤتمر ECOOP '98، قدّم شري رام كريشنامورثي وآخرون [ 4 ] حلاً قائماً على أنماط التصميم لمشكلة توسيع لغة برمجة تعبيرية ومجموعة أدواتها في آنٍ واحد. أطلقوا عليها اسم "مشكلة التعبيرية" لاعتقادهم أن مصممي لغات البرمجة يمكنهم استخدامها لإظهار القدرة التعبيرية لإبداعاتهم. بالنسبة لـ PLT، ظهرت المشكلة أثناء بناء DrScheme، المعروفة الآن باسم DrRacket ، وقد حلّوها [ 5 ] من خلال إعادة اكتشاف mixins . [ 6 ] [ 7 ] ولتجنب استخدام مشكلة لغة برمجة في ورقة بحثية تتناول لغات البرمجة، استخدم كريشنامورثي وآخرون مشكلة برمجة هندسية قديمة لشرح حلهم القائم على الأنماط. في محادثات مع فيليسين وكريشنامورثي بعد عرض ECOOP، أدرك وادلر الطبيعة المتمحورة حول لغات البرمجة للمشكلة، وأشار إلى أن حل كريشنامورثي استخدم تحويل النوع لتجاوز نظام أنواع جافا. استمر النقاش على قائمة بريدية خاصة بالأنواع، حيث أوضح كوركي كارترايت (رايس) وكيم بروس (ويليامز) كيف يمكن لأنظمة الأنواع في لغات البرمجة الكائنية التوجه أن تُزيل هذا العيب. ردًا على ذلك، صاغ وادلر مقالته مُحددًا التحدي: "إن قدرة اللغة على حل مشكلة التعبير تُعد مؤشرًا بارزًا على قدرتها على التعبير". ويُشير مصطلح "مشكلة التعبير" إلى تورية بين "التعبير" بمعنى "مدى قدرة لغتك على التعبير" و"التعبير" بمعنى "المصطلحات التي تحاول تمثيلها هي تعبيرات لغوية".

وقد اكتشف آخرون متغيرات لمشكلة التعبير في نفس الوقت تقريبًا الذي اكتشف فيه PLT الخاص بجامعة رايس، ولا سيما توماس كوهن [ 8 ] في أطروحته، وسماراجداكيس وباتوري [ 9 ] في مقال موازٍ في ECOOP 98.

استخدمت بعض الدراسات اللاحقة مشكلة التعبير لعرض قوة تصميمات لغات البرمجة. [ 10 ] [ 11 ]

تُعد مشكلة التعبير أيضًا مشكلة أساسية في تصميم خط إنتاج البرمجيات متعدد الأبعاد، وعلى وجه الخصوص كتطبيق أو حالة خاصة لمكعبات برنامج FOSD .

الحلول

توجد حلول متنوعة لمشكلة التعبير. ويختلف كل حل في مقدار التعليمات البرمجية التي يجب على المستخدم كتابتها لتنفيذه، وفي ميزات اللغة التي يتطلبها.

مثال

وصف المشكلة

يمكننا أن نتخيل أننا لا نملك الشفرة المصدرية للمكتبة التالية، المكتوبة بلغة C# ، والتي نرغب في توسيعها:

واجهة IEvalExp{int Eval ();}class Lit : IEvalExp{داخلي مضاء ( عدد صحيح n ){N = n ;}int داخلي N { get ; }public int Eval (){أعد N ؛}}إضافة فئة : IEvalExp{إضافة داخلية ( IEvalExp يسار ، IEvalExp يمين ){اليسار = اليسار ؛يمين = يمين ؛}internal IEvalExp Left { get ; }internal IEvalExp Right { get ; }public int Eval (){أعد قيمة Left.Eval ( ) + قيمة Right.Eval ( ) .}}فئة ثابتة ExampleOne{static IEvalExp AddOneAndTwo () => new Add ( new Lit ( 1 ), new Lit ( 2 ));static int EvaluateTheSumOfOneAndTwo () => AddOneAndTwo (). Eval ();}

باستخدام هذه المكتبة، يمكننا التعبير عن التعبير الحسابي 1 + 2كما فعلنا سابقًا، ExampleOne.AddOneAndTwo()ويمكننا تقييم التعبير باستدعاء الدالة المناسبة .Eval(). الآن، تخيل أننا نرغب في توسيع هذه المكتبة، فإضافة نوع جديد أمر سهل لأننا نعمل بلغة برمجة كائنية التوجه . على سبيل المثال، يمكننا إنشاء الفئة التالية:

class Multi : IEvalExp{مضاعف داخلي ( IEvalExp يسار ، IEvalExp يمين ){اليسار = اليسار ؛يمين = يمين ؛}internal IEvalExp Left { get ; }internal IEvalExp Right { get ; }public int Eval (){أعد قيمة Left.Eval ( ) * قيمة Right.Eval ( ) .}}

مع ذلك، إذا أردنا إضافة دالة جديدة إلى نوع معين (طريقة جديدة في مصطلحات لغة C#)، مثلاً لتنسيق تعبير ما بشكل جميل، فعلينا تغيير الواجهة IEvalExpثم تعديل جميع الفئات التي تُنفذها. خيار آخر هو إنشاء واجهة جديدة تُوسّع الواجهة الأصلية IEvalExp، ثم إنشاء أنواع فرعية للفئات Lit، ولكن التعبير المُعاد من هذه الواجهة يكون قد تم تجميعه بالفعل، لذا لن نتمكن من استخدام الدالة الجديدة على النوع القديم. ينعكس هذا الوضع في لغات البرمجة الوظيفية مثل F#، حيث يسهل إضافة دالة إلى نوع معين، لكن توسيع الأنواع أو إضافة أنواع جديدة إليها أمر صعب.AddMultExampleOne.AddOneAndTwo()

حل المسائل باستخدام الجبر الكائني

دعونا نعيد تصميم المكتبة الأصلية مع مراعاة قابلية التوسع باستخدام الأفكار الواردة في ورقة " قابلية التوسع للجميع". [ 17 ]

واجهة الجبر التجريبي < T >{T Lit ( int n );T Add ( T left , T right );}class ExpFactory : ExpAlgebra < IEvalExp >{public IEvalExp Lit ( int n ){أعد كائن Lit جديدًا ( n }public IEvalExp Add ( IEvalExp left , IEvalExp right ){أعد إضافة جديدة ( يسار ، يمين }}فئة ثابتة ExampleTwo < T >{public static T AddOneToTwo ( ExpAlgebra < T > ae ) => ae . Add ( ae . Lit ( 1 ), ae . Lit ( 2 ));}

نستخدم نفس التنفيذ كما في مثال الكود الأول، ولكننا نضيف الآن واجهة جديدة تحتوي على الدوال الخاصة بالنوع، بالإضافة إلى مصنع للجبر. لاحظ أننا الآن نُنشئ التعبير باستخدام ExampleTwo.AddOneToTwo()الواجهة ExpAlgebra<T>بدلاً من إنشائه مباشرةً من الأنواع. يمكننا الآن إضافة دالة بتوسيع الواجهة ExpAlgebra<T>، وسنضيف وظيفة لطباعة التعبير.

واجهة IPrintExp : IEvalExp{string Print ();}class PrintableLit : Lit , IPrintExp{printableLit داخلي ( عدد صحيح n ) : قاعدة ( n ){N = n ;}int داخلي N { get ; }public string Print (){أعد N.ToString ( ) ;}}class PrintableAdd : Add , IPrintExp{internal PrintableAdd ( IPrintExp left , IPrintExp right ) : base ( left , right ){اليسار = اليسار ؛يمين = يمين ؛}داخلي جديد IPrintExp Left { get ; }داخلي جديد IPrintExp Right { get ; }public string Print (){return Left.Print () + " + " + Right.Print ( ) ;}}class PrintFactory : ExpFactory , ExpAlgebra < IPrintExp >{public IPrintExp Add ( IPrintExp left , IPrintExp right ){أعد كائن PrintableAdd جديدًا ( يسار ، يمين }public new IPrintExp Lit ( int n ){أعد كائن PrintableLit جديدًا ( n }}فئة ثابتة ExampleThree{internal static int Evaluate () => ExampleTwo < IPrintExp > . AddOneToTwo ( new PrintFactory ()). Eval ();internal static string Print () => ExampleTwo < IPrintExp > . AddOneToTwo ( new PrintFactory ()). Print ();}

لاحظ أننا ExampleThree.Print()نطبع تعبيرًا تم تجميعه مسبقًا ExampleTwo، ولم نكن بحاجة لتعديل أي كود موجود. لاحظ أيضًا أن هذا التعبير لا يزال مُحدد النوع بدقة، فلا نحتاج إلى استخدام الانعكاس أو التحويل. إذا استبدلنا التعبير PrintFactory()بتعبير آخر ExpFactory()في الكود، ExampleThree.Print()فسنحصل على خطأ في التجميع لأن .Print()الدالة غير موجودة في هذا السياق.

انظر أيضاً

مراجع

  1. "مشكلة التعبير" .
  2. رينولدز، جون سي. (1975). "الأنواع المعرفة من قبل المستخدم وهياكل البيانات الإجرائية كنهج تكميلي لتجريد البيانات". اتجاهات جديدة في لغات الخوارزميات (ملف PDF) . مجموعة عمل الاتحاد الدولي لمعالجة المعلومات 2.1 حول لغة ألغول. الصفحات 157-168 . 
  3. كوك، ويليام (1990). "البرمجة الكائنية التوجه مقابل أنواع البيانات المجردة" . في: باكر، جيه دبليو دي؛ روفر، دبليو بي دي؛ روزنبرغ، جي (محررون). أسس لغات البرمجة الكائنية التوجه (FOOL)، مدرسة/ورشة عمل REX . سلسلة محاضرات في علوم الحاسوب. المجلد 489. نوردويكيرهاوت، هولندا: سبرينغر برلين هايدلبرغ. الصفحات 151-178 . doi : 10.1007/BFb0019443 . ISBN   978-3-540-46450-1.
  4. "دمج التصميم الموجه للكائنات والتصميم الوظيفي لتعزيز إعادة الاستخدام" .
  5. فايندلر، روبرت بروس؛ فلات، ماثيو (1999). "البرمجة المعيارية الموجهة للكائنات باستخدام الوحدات والمزيجات" . إشعارات ACM SIGPLAN . 34 : 94-104 . doi : 10.1145/291251.289432 .
  6. كوك، ويليام (1989). دلالات دلالية للوراثة (ملف PDF) (أطروحة دكتوراه). جامعة براون.
  7. فلات، ماثيو؛ كريشنامورثي، شري رام؛ فيليسين، ماتياس (1998). "الفئات والمزيجات". وقائع الندوة الخامسة والعشرين لجمعية ACM SIGPLAN-SIGACT حول مبادئ لغات البرمجة - POPL '98 . الصفحات 171-183 . doi : 10.1145/268946.268961 . ISBN  978-0897919791. S2CID 5815257 . 
  8. كوهن، توماس (1999). نظام أنماط وظيفية للتصميم الموجه للكائنات . دارمشتات: دار نشر دكتور كوفاتش. ISBN 978-3-86064-770-7.
  9. سماراغداكيس، يانيس؛ دون باتوري (1998). تنفيذ مكونات قابلة لإعادة الاستخدام موجهة للكائنات . سلسلة محاضرات في علوم الحاسوب. المجلد 1445. 
  10. زينجر، ماتياس؛ أوديرسكي، مارتن (2001). "أنواع البيانات الجبرية القابلة للتوسيع مع القيم الافتراضية". وقائع المؤتمر الدولي السادس لجمعية ACM SIGPLAN حول البرمجة الوظيفية . الصفحات 241-252 . CiteSeerX 10.1.1.28.6778 . doi : 10.1145/507635.507665 . ISBN   1-58113-415-0.
  11. زينجر، ماتياس؛ أوديرسكي، مارتن (2005). "حلول قابلة للتوسيع بشكل مستقل لمسألة التعبير" (ملف PDF) . مؤتمر FOOL 2005. جمعية آلات الحوسبة. CiteSeerX 10.1.1.107.4449 . 
  12. تشامبرز، كريج؛ ليفنز، غاري تي. (نوفمبر 1995). "التحقق من النوع والوحدات النمطية للأساليب المتعددة" . معاملات ACM في لغات البرمجة والأنظمة . 17 (6): 805-843 . doi : 10.1145/218570.218571 .
  13. كليفتون، كورتيس؛ ليفنز، غاري تي؛ تشامبرز، كريغ؛ ميلستين، تود (2000). "MultiJava: فئات مفتوحة معيارية وإرسال متعدد متناظر للغة جافا". وقائع المؤتمر الخامس عشر لجمعية ACM SIGPLAN حول البرمجة الكائنية والأنظمة واللغات والتطبيقات (ملف PDF) . الصفحات 130-145 . doi : 10.1145/353171.353181 . ISBN  978-1-58113-200-7. S2CID 7879645 . 
  14. ووتر سويرسترا (2008). "أنواع البيانات حسب الطلب" . مجلة البرمجة الوظيفية . 18 (4). مطبعة جامعة كامبريدج: 423-436 . doi : 10.1017/S0956796808006758 . ISSN 0956-7968 . S2CID 21038598 .  
  15. وير، ستيفان؛ ثيمان، بيتر (يوليو 2011). "JavaGI: تفاعل فئات الأنواع مع الواجهات والوراثة" . معاملات ACM في لغات البرمجة والأنظمة . 33 (4): 1-83 . doi : 10.1145/1985342.1985343 . S2CID 13174506 . 
  16. كاريت، جاك؛ كيسليوف، أوليغ؛ تشونغ-تشيه، شان (2009). "مترجمات مرحلية بدون علامات، مُقَيَّمة جزئيًا: مترجمات مرحلية بدون علامات للغات ذات أنواع أبسط" (ملف PDF) . مجلة البرمجة الوظيفية . 19 (5): 509-543 . doi : 10.1017/S0956796809007205 . S2CID 6054319 . 
  17. 12Oliveira, Bruno C. d. S.; Cook, William R. (2012). "Extensibility for the Masses: Practical Extensibility with Object Algebras"(PDF). Ecoop '12.
  18. Garrigue, Jacques (2000). "Code Reuse Through Polymorphic Variants"(PDF). Workshop on Foundations of Software Engineering. Sasaguri, Japan, November 2000. CiteSeerX 10.1.1.128.7169.