تعدد الأشكال (علوم الكمبيوتر)
| تعدد الأشكال |
|---|
| تعدد الأشكال المخصص |
| تعدد الأشكال البارامترية |
| التصنيف الفرعي |
في نظرية لغة البرمجة ونظرية النوع ، تعدد الأشكال هو استخدام رمز واحد لتمثيل أنواع متعددة مختلفة. [1]
في البرمجة الموجهة للكائنات ، تعدد الأشكال هو توفير واجهة واحدة لكيانات من أنواع بيانات مختلفة . [2] تم استعارة المفهوم من مبدأ في علم الأحياء حيث يمكن للكائن الحي أو النوع أن يكون له أشكال أو مراحل مختلفة عديدة. [3]
الأشكال الرئيسية الأكثر شيوعًا والمعترف بها من تعدد الأشكال هي:
- تعدد الأشكال المخصص : يحدد واجهة مشتركة لمجموعة عشوائية من الأنواع المحددة بشكل فردي.
- تعدد الأشكال المعلمية : عدم تحديد أنواع محددة واستخدام رموز مجردة بدلاً من ذلك يمكنها أن تحل محل أي نوع.
- التصنيف الفرعي (يسمى أيضًا تعدد أشكال النوع الفرعي أو تعدد أشكال التضمين ): عندما يشير الاسم إلى حالات من العديد من الفئات المختلفة المرتبطة ببعض الفئات العليا المشتركة. [4]
تاريخ
تطور الاهتمام بأنظمة الأنواع المتعددة الأشكال بشكل كبير في تسعينيات القرن العشرين، حيث بدأت التطبيقات العملية في الظهور بحلول نهاية العقد. تم وصف تعدد الأشكال المخصص وتعدد الأشكال البارامترية في الأصل في المفاهيم الأساسية في لغات البرمجة لكريستوفر ستراتشي ، [ 5] حيث تم إدراجهما على أنهما "الفئتان الرئيسيتان" لتعدد الأشكال. كان تعدد الأشكال المخصص سمة من سمات ALGOL 68 ، بينما كان تعدد الأشكال البارامترية السمة الأساسية لنظام نوع ML .
في ورقة بحثية عام 1985، قدم بيتر ويجنر ولوكا كارديلي مصطلح تعدد أشكال التضمين لنمذجة الأنواع الفرعية والوراثة ، [1] مشيرين إلى سيمولا باعتبارها أول لغة برمجة تنفذه.
الاستمارات
تعدد الأشكال المخصص
اختار كريستوفر ستراتشي مصطلح تعدد الأشكال المخصص للإشارة إلى الدوال المتعددة الأشكال التي يمكن تطبيقها على حجج من أنواع مختلفة، ولكنها تتصرف بشكل مختلف اعتمادًا على نوع الحجة التي يتم تطبيقها عليها (المعروفة أيضًا باسم التحميل الزائد للوظيفة أو التحميل الزائد للمشغل ). [5] مصطلح " مخصص " في هذا السياق ليس مهينًا: بدلاً من ذلك، يعني أن هذا الشكل من تعدد الأشكال ليس سمة أساسية لنظام النوع. في مثال Java أدناه، يبدو أن الدوال تعملAdd بشكل عام على نوعين ( عدد صحيح وسلسلة ) عند النظر في الاستدعاءات، ولكن يعتبرها المترجم دالتين متميزتين تمامًا لجميع المقاصد والأغراض:
الفئة AdHocPolymorphic { public String add ( int x , int y ) { return "Sum: " + ( x + y ); }
public String add ( String name ) { return "Added" + name ; } }
الفئة العامة adhoc { public static void main ( String [] args ) { AdHocPolymorphic poly = new AdHocPolymorphic ();
System.out.println ( poly.add ( 1 , 2 ) ) ; // يطبع "المجموع: 3" System.out.println ( poly.add ( " Jay " ) ) ) ; // يطبع " تمت إضافة Jay " } }
في اللغات ذات النوع الديناميكي ، قد يكون الموقف أكثر تعقيدًا حيث قد لا تكون الوظيفة الصحيحة التي يجب استدعاؤها قابلة للتحديد إلا في وقت التشغيل.
تم تعريف تحويل النوع الضمني أيضًا على أنه شكل من أشكال تعدد الأشكال، ويشار إليه باسم "تعدد أشكال الإكراه". [1] [6]
تعدد الأشكال البارامترية
يسمح تعدد الأشكال البارامترية بكتابة دالة أو نوع بيانات بشكل عام، بحيث يمكنها التعامل مع القيم بشكل موحد دون الاعتماد على نوعها. [7] تعدد الأشكال البارامترية هي طريقة لجعل اللغة أكثر تعبيرًا مع الحفاظ على سلامة النوع الثابت الكامل .
ينطبق مفهوم تعدد الأشكال البارامترية على كل من أنواع البيانات والوظائف . تُعرف الوظيفة التي يمكن تقييمها أو تطبيقها على قيم من أنواع مختلفة باسم وظيفة متعددة الأشكال. يُطلق على نوع البيانات الذي يمكن أن يبدو وكأنه من نوع معمم (على سبيل المثال، قائمة تحتوي على عناصر من نوع عشوائي) نوع بيانات متعدد الأشكال مثل النوع المعمم الذي يتم من خلاله إنشاء مثل هذه التخصصات.
تعد تعدد الأشكال البارامترية منتشرة في البرمجة الوظيفية، حيث يشار إليها غالبًا ببساطة باسم "تعدد الأشكال". يوضح المثال التالي في Haskell نوع بيانات قائمة ذات معلمات ودالتين متعددتي الأشكال بارامتريًا عليها:
البيانات القائمة أ = لا شيء | سلبيات أ ( القائمة أ )
الطول :: القائمة أ -> عدد صحيح الطول لا شيء = 0 الطول ( سلبيات x xs ) = 1 + الطول xs
الخريطة :: ( أ -> ب ) -> القائمة أ -> القائمة ب الخريطة ف لا شيء = لا شيء الخريطة ف ( سلبيات x xs ) = سلبيات ( f x ) ( الخريطة ف xs )
تتوفر أيضًا تعدد الأشكال البارامترية في العديد من لغات البرمجة الموجهة للكائنات. على سبيل المثال، القوالب في C++ و D ، أو تحت اسم generics في C# و Delphi وJava و Go :
الفئة List < T > { الفئة Node < T > { T elem ; Node < T > next ; } Node < T > head ; int length () { ... } }
قائمة < B > map ( Func < A , B > f , List < A > xs ) { ... }
قام جون سي رينولدز (ولاحقًا جان إيف جيرارد ) بتطوير مفهوم تعدد الأشكال رسميًا كامتداد لحساب لامدا (يُسمى حساب لامدا متعدد الأشكال أو نظام F ). أي دالة متعددة الأشكال بارامتريًا مقيدة بالضرورة فيما يمكنها فعله، حيث تعمل على شكل البيانات بدلاً من قيمتها، مما يؤدي إلى مفهوم المعلمة .
التصنيف الفرعي
تستخدم بعض اللغات فكرة التصنيف الفرعي (وتسمى أيضًا تعدد أشكال النوع الفرعي أو تعدد أشكال التضمين ) لتقييد نطاق الأنواع التي يمكن استخدامها في حالة معينة من تعدد الأشكال. في هذه اللغات، يسمح التصنيف الفرعي بكتابة دالة لتأخذ كائنًا من نوع معين T ، ولكنها تعمل أيضًا بشكل صحيح، إذا تم تمرير كائن ينتمي إلى نوع S وهو نوع فرعي من T (وفقًا لمبدأ استبدال Liskov ). تُكتب علاقة النوع هذه أحيانًا S <: T. وعلى العكس من ذلك، يُقال إن T هو نوع فرعي من S ، ويُكتب T :> S. عادةً ما يتم حل تعدد أشكال النوع الفرعي ديناميكيًا (انظر أدناه).
في مثال Java التالي، يتم تحويل القطط والكلاب إلى أنواع فرعية من الحيوانات الأليفة. تقبل العملية letsHear()حيوانًا أليفًا، ولكنها ستعمل بشكل صحيح أيضًا إذا تم تمرير نوع فرعي إليه:
فئة مجردة Pet { سلسلة مجردة speak (); }
الفئة Cat تمتد إلى Pet { String speak () { return "Meow!" ; } }
الفئة Dog تمتد إلى Pet { String speak () { return "Woof!" ; } }
void ثابت letsHear ( الحيوانات الأليفة النهائية pet ) { println ( pet.speak ( ) ) ; }
ثابت void main ( String [] args ) { letsHear ( new Cat ()); letsHear ( new Dog ()); }
في مثال آخر، إذا كانت Number و Rational و Integer أنواعًا بحيث Number :> Rational و Number :> Integer ( Rational و Integer كأنواع فرعية لنوع Number الذي يعد نوعًا فرعيًا منهما)، فإن الدالة المكتوبة لأخذ Number ستعمل بشكل جيد بنفس القدر عند تمرير Integer أو Rational كما هو الحال عند تمرير Number . يمكن إخفاء النوع الفعلي للكائن عن العملاء في صندوق أسود ، والوصول إليه عبر هوية الكائن . إذا كان نوع Number مجردًا ، فقد لا يكون من الممكن حتى وضع يديك على كائن يكون نوعه الأكثر اشتقاقًا هو Number (انظر نوع البيانات المجردة ، والفئة المجردة ). يُعرف هذا النوع المعين من التسلسل الهرمي للأنواع، وخاصة في سياق لغة Scheme ، باسم البرج العددي ، وعادةً ما يحتوي على أنواع أكثر بكثير.
توفر لغات البرمجة الموجهة للكائنات تعدد أشكال النوع الفرعي باستخدام التصنيف الفرعي (المعروف أيضًا باسم الميراث ). في التنفيذات النموذجية، تحتوي كل فئة على ما يسمى بالجدول الافتراضي (يُطلق عليه باختصار vtable ) - وهو جدول من الوظائف التي تنفذ الجزء المتعدد الأشكال من واجهة الفئة - ويحتوي كل كائن على مؤشر إلى vtable لفئته، والذي يتم استشارته بعد ذلك كلما تم استدعاء طريقة متعددة الأشكال. هذه الآلية هي مثال على:
- الربط المتأخر ، لأن استدعاءات الوظيفة الافتراضية لا يتم ربطها إلا في وقت الاستدعاء؛
- إرسال واحد (أي تعدد أشكال الوسيطة الواحدة)، لأن استدعاءات الوظائف الافتراضية مرتبطة ببساطة من خلال النظر في الجدول الافتراضي الذي توفره الوسيطة الأولى (الكائن
this)، وبالتالي فإن أنواع وقت التشغيل للحجج الأخرى غير ذات صلة تمامًا.
ينطبق نفس الأمر على معظم أنظمة الكائنات الشائعة الأخرى. ومع ذلك، توفر بعضها، مثل Common Lisp Object System ، عمليات إرسال متعددة ، حيث تكون استدعاءات الطريقة متعددة الأشكال في جميع الوسائط.
التفاعل بين تعدد الأشكال البارامترية والتصنيف الفرعي يؤدي إلى مفهومي التباين والقياس الكمي المحدود .
تعدد أشكال الصف
تعد تعدد أشكال الصفوف [8] مفهومًا مشابهًا ولكنه مختلف عن التصنيف الفرعي. فهو يتعامل مع الأنواع البنيوية . ويسمح باستخدام جميع القيم التي تحتوي أنواعها على خصائص معينة، دون فقدان معلومات النوع المتبقية.
تعدد الأنماط
المفهوم المرتبط بهذا هو تعدد الأنماط (أو عمومية نوع البيانات ). تكون الدالة متعددة الأنماط أكثر عمومية من الدالة متعددة الأشكال، وفي مثل هذه الدالة، "على الرغم من أنه يمكن للمرء توفير حالات مخصصة ثابتة لأنواع بيانات محددة، إلا أن مجموعة مخصصة غائبة". [9]
تعدد أشكال الرتبة
تعد تعدد أشكال الرتبة واحدة من السمات المميزة للغات برمجة المصفوفات ، مثل APL . إن جوهر نموذج برمجة تعدد أشكال الرتبة هو التعامل ضمناً مع جميع العمليات كعمليات مجمعة، يمكن استخدامها على المصفوفات ذات الأبعاد العديدة بشكل تعسفي، [10] وهذا يعني أن تعدد أشكال الرتبة يسمح بتعريف الوظائف للعمل على المصفوفات من أي شكل وحجم.
جوانب التنفيذ
تعدد الأشكال الثابتة والديناميكية
يمكن تمييز تعدد الأشكال من خلال وقت تحديد التنفيذ: بشكل ثابت (في وقت التجميع) أو ديناميكيًا (في وقت التشغيل، عادةً عبر وظيفة افتراضية ). يُعرف هذا على التوالي بالإرسال الثابت والإرسال الديناميكي ، وتسمى الأشكال المقابلة من تعدد الأشكال وفقًا لذلك تعدد الأشكال الثابت وتعدد الأشكال الديناميكي .
يتم تنفيذ تعدد الأشكال الثابت بشكل أسرع، لأنه لا توجد تكلفة إرسال ديناميكية، لكنه يتطلب دعمًا إضافيًا للمترجم. علاوة على ذلك، يسمح تعدد الأشكال الثابت بتحليل ثابت أكبر بواسطة المترجمين (خاصة للتحسين)، وأدوات تحليل التعليمات البرمجية المصدرية، والقراء البشريين (المبرمجين). تعدد الأشكال الديناميكي أكثر مرونة ولكنه أبطأ - على سبيل المثال، يسمح تعدد الأشكال الديناميكي بالكتابة البطيئة، وقد تعمل مكتبة مرتبطة ديناميكيًا على الكائنات دون معرفة نوعها الكامل.
يحدث تعدد الأشكال الساكن عادة في تعدد الأشكال المخصص وتعدد الأشكال البارامترية، في حين أن تعدد الأشكال الديناميكي شائع في تعدد أشكال النوع الفرعي. ومع ذلك، من الممكن تحقيق تعدد الأشكال الساكن مع التصنيف الفرعي من خلال استخدام أكثر تعقيدًا لبرمجة القالب ، أي نمط القالب المتكرر بشكل غريب .
عندما يتم الكشف عن تعدد الأشكال من خلال مكتبة ، يصبح تعدد الأشكال الثابت مستحيلًا بالنسبة للمكتبات الديناميكية حيث لا توجد طريقة لمعرفة أنواع المعلمات عند بناء الكائن المشترك . بينما تستخدم لغات مثل C++ وRust قوالب أحادية الشكل ، فإن لغة برمجة Swift تستخدم بشكل مكثف الإرسال الديناميكي لبناء واجهة ثنائية للتطبيق لهذه المكتبات بشكل افتراضي. ونتيجة لذلك، يمكن مشاركة المزيد من التعليمات البرمجية لتقليل حجم النظام على حساب النفقات العامة لوقت التشغيل. [11]
انظر أيضا
مراجع
- ^ abc Cardelli, Luca ; Wegner, Peter (December 1985). "On understanding types, data abstraction, and polymorphism" (PDF) . ACM Computing Surveys . 17 (4): 471–523. CiteSeerX 10.1.1.117.695 . doi :10.1145/6041.6042. S2CID 2921816."الأنواع المتعددة الأشكال هي أنواع تكون عملياتها قابلة للتطبيق على قيم أكثر من نوع واحد."
- ^ Stroustrup, Bjarne (19 فبراير 2007). "Bjarne Stroustrup's C++ Glossary".
تعدد الأشكال – توفير واجهة واحدة للكيانات من أنواع مختلفة.
- ^ "تعدد الأشكال". دروس جافا: تعلم لغة جافا: الواجهات والوراثة . أوراكل . تم الاسترجاع في 2021-09-08 .
- ^ كونالين، جيه؛ إنجل، إم؛ هيوستن، كيه؛ ماكسيمشوك، آر؛ يونج، بي؛ بوش، جي . (2007). التحليل والتصميم الموجه للكائنات مع التطبيقات (الطبعة الثالثة). بيرسون للتعليم. رقم ISBN 9780132797443.
- ^ ab Strachey, Christopher (2000). "المفاهيم الأساسية في لغات البرمجة". الحساب الرمزي والحوسبة ذات الترتيب الأعلى . 13 (1/2): 11–49. CiteSeerX 10.1.1.332.3161 . doi :10.1023/A:1010000313106. ISSN 1573-0557. S2CID 14124601.
- ^ Tucker, Allen B. (2004). Computer Science Handbook (الطبعة الثانية). Taylor & Francis. ص 91–. ISBN 978-1-58488-360-9.
- ^ Pierce, BC (2002). "23.2 Varieties of Polymorphism". Types and Programming Languages . MIT Press. ص 340-1. ISBN 9780262162098.
- ^ واند، ميتشل (يونيو 1989). "استدلال النوع لتسلسل السجلات والوراثة المتعددة". وقائع الندوة السنوية الرابعة حول المنطق في علوم الكمبيوتر . ص 92-97. doi :10.1109/LICS.1989.39162.
- ^ Lämmel, Ralf; Visser, Joost (2002). "Typed Combinators for Generic Traversal". Practical Aspects of Declarative Languages: 4th International Symposium . Springer. ص. 137–154، انظر ص. 153. CiteSeerX 10.1.1.18.5727 . ISBN 354043092X.
- ^ Slepak, Justin; Shivers, Olin; Manolios, Panagiotis (2019). "دلالات تعدد أشكال الرتب". arXiv : 1907.00509 [cs.PL].
- ^ Beingessner, Alexis. "كيف حقق Swift الربط الديناميكي حيث لم يستطع Rust ذلك".
روابط خارجية
- أمثلة على تعدد الأشكال في لغة C++
- الأشياء والتعدد الشكلي (Visual Prolog)
- تعدد الأشكال على MSDN
- توثيق تعدد الأشكال في Java على Oracle
