تعدد الأشكال (علوم الحاسوب)

في نظرية لغات البرمجة ونظرية الأنواع ، يسمح تعدد الأشكال بأن يكون للقيمة أو المتغير أكثر من نوع واحد، كما يسمح بإجراء عملية معينة على قيم من أكثر من نوع واحد. [ 1 ]

في البرمجة كائنية التوجه ، يُعرَّف تعدد الأشكال بأنه توفير واجهة واحدة لكيانات ذات أنواع بيانات مختلفة . [ 2 ] هذا المفهوم مُستعار من مبدأ في علم الأحياء، حيث يمكن للكائن الحي أو النوع أن يتخذ أشكالًا أو مراحل مختلفة. [ 3 ]

أكثر أشكال التعدد الشكلي شيوعاً هي:

  • تعدد الأشكال المخصص : يحدد واجهة مشتركة لمجموعة عشوائية من الأنواع المحددة بشكل فردي.
  • التعدد الشكلي البارامتري : لا يحدد أنواعًا ملموسة ويستخدم بدلاً من ذلك رموزًا مجردة يمكن أن تحل محل أي نوع.
  • التنميط الفرعي (يسمى أيضًا تعدد الأشكال الفرعي أو تعدد الأشكال التضميني ): عندما يشير اسم ما إلى مثيلات للعديد من الفئات المختلفة المرتبطة بفئة أساسية مشتركة. [ 4 ]

تاريخ

ازداد الاهتمام بأنظمة الأنواع متعددة الأشكال بشكل ملحوظ في تسعينيات القرن الماضي، وبدأت تطبيقاتها العملية بالظهور مع نهاية العقد. وُصِفَ تعدد الأشكال المخصص وتعدد الأشكال البارامتري لأول مرة في كتاب كريستوفر ستراشي " المفاهيم الأساسية في لغات البرمجة" [ 5 ] ، حيث ذُكِرَا على أنهما "الفئتان الرئيسيتان" لتعدد الأشكال. كان تعدد الأشكال المخصص سمةً من سمات لغة ALGOL 68 ، بينما كان تعدد الأشكال البارامتري السمة الأساسية لنظام أنواع لغة ML .

في ورقة بحثية عام 1985، قدم بيتر ويجنر ولوكا كارديلي مصطلح تعدد الأشكال التضميني لنمذجة الأنواع الفرعية والوراثة ، [ 1 ] مستشهدين بـ Simula باعتبارها أول لغة برمجة تقوم بتنفيذها.

النماذج

تعدد الأشكال المخصص

اختار كريستوفر ستراشي مصطلح "تعدد الأشكال المخصص" للإشارة إلى الدوال متعددة الأشكال التي يمكن تطبيقها على وسائط من أنواع مختلفة، ولكنها تتصرف بشكل مختلف اعتمادًا على نوع الوسيط الذي تُطبق عليه (المعروف أيضًا باسم تحميل الدوال أو تحميل المعاملات ). [ 5 ] تعني كلمة "مخصص" هنا أن هذا النوع من تعدد الأشكال ليس سمة أساسية لنظام الأنواع. في مثال جافا أدناه، addتبدو الدوال وكأنها تعمل بشكل عام على نوعين ( عدد صحيح وسلسلة نصية ) عند النظر إلى الاستدعاءات، ولكن يعتبرها المترجم دوالًا مختلفة تمامًا .

class AdHocPolymorphic { public String add ( int x , int y ) { return String . format ( "المجموع: %d" , x + y ); }public String add ( String name ) { return String . format ( "تمت الإضافة " , name ); } }public class 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 " ) ) ; // يطبع " تمت إضافة جاي " } }

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

وقد عُرِّف التحويل الضمني للأنواع أيضاً بأنه شكل من أشكال تعدد الأشكال، ويُشار إليه باسم "تعدد الأشكال القسري". [ 1 ] [ 6 ]

تعدد الأشكال البارامتري

يُتيح التعدد الشكلي البارامتري كتابة دالة أو نوع بيانات بشكل عام، بحيث يمكنه التعامل مع القيم بشكل موحد دون الاعتماد على نوعها. [ 7 ] يُعد التعدد الشكلي البارامتري وسيلة لجعل اللغة أكثر تعبيرًا مع الحفاظ على سلامة النوع الثابتة الكاملة .

ينطبق مفهوم التعدد الشكلي البارامتري على كلٍ من أنواع البيانات والدوال . تُعرف الدالة التي يمكن تقييمها أو تطبيقها على قيم من أنواع مختلفة بالدالة متعددة الأشكال. يُصنف نوع البيانات الذي يمكن أن يبدو كنوع عام (مثل قائمة تحتوي على عناصر من أي نوع) كنوع بيانات متعدد الأشكال، تمامًا مثل النوع العام الذي تُشتق منه هذه التخصيصات.

يُعدّ التعدد الشكلي البارامتري شائعًا في البرمجة الوظيفية، حيث يُشار إليه غالبًا ببساطة باسم "التعدد الشكلي". يُظهر المثال التالي في لغة هاسكل نوع بيانات قائمة مُعاملة ودالتين متعددتي الأشكال بارامتريًا عليها:

data List a = Nil | Cons a ( List a )الطول :: قائمة أ -> عدد صحيح الطول Nil = 0 طول ( Cons x xs ) = 1 + طول xsmap :: ( a -> b ) -> List a -> List b map f Nil = Nil map f ( Cons x xs ) = Cons ( f x ) ( map f xs )

يتوفر تعدد الأشكال البارامتري أيضًا في العديد من لغات البرمجة الكائنية. على سبيل المثال، القوالب في لغتي C++ و D ، أو تحت اسم الأنواع العامة في لغات C# و Delphi و Java و Go :

class List <T> { class Node <T> { T elem ; Node <T> next ; } Node <T> head ; int length ( ) { ... } }List <B> map ( Func < A , B > f , List <A> xs ) { ... }

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

التصنيف الفرعي

تستخدم بعض لغات البرمجة مفهوم التفرع (أو تعدد الأشكال الفرعي أو تعدد الأشكال التضميني ) لتقييد نطاق الأنواع التي يمكن استخدامها في حالة معينة من تعدد الأشكال. في هذه اللغات، يسمح التفرع بكتابة دالة تأخذ كائنًا من نوع معين T ، ولكنها تعمل أيضًا بشكل صحيح إذا تم تمرير كائن ينتمي إلى نوع S وهو نوع فرعي من T (وفقًا لمبدأ استبدال ليسكوف ). تُكتب علاقة النوع هذه أحيانًا S <: T. وعلى العكس، يُقال إن T نوع فائق لـ S ، ويُكتب T  :> S. عادةً ما يتم حل تعدد الأشكال الفرعي ديناميكيًا (انظر أدناه).

في مثال جافا التالي، تُعتبر القطط والكلاب أنواعًا فرعية من الحيوانات الأليفة. يقبل الإجراء letsHear()حيوانًا أليفًا، ولكنه سيعمل بشكل صحيح أيضًا إذا تم تمرير نوع فرعي إليه:

فئة مجردة Pet { سلسلة مجردة speak (); }class Cat extends Pet { String speak () { return "مياو!" ; } }class Dog extends Pet { String speak () { return "نباح!" ; } }static void letsHear ( final Pet pet ) { System . out . println ( pet . speak ()); }static 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 (انظر نوع البيانات المجرد ، والفئة المجردة ). يُعرف هذا النوع من التسلسل الهرمي للأنواع، خاصةً في سياق لغة Scheme ، باسم " البرج العددي" ، وعادةً ما يحتوي على أنواع أكثر بكثير.

تُتيح لغات البرمجة كائنية التوجه تعدد الأشكال الفرعية باستخدام التوريث (المعروف أيضًا باسم الوراثة ). في التطبيقات النموذجية، تحتوي كل فئة على ما يُسمى بالجدول الافتراضي (يُشار إليه اختصارًا بـ vtable ) - وهو جدول للدوال التي تُنفذ الجزء متعدد الأشكال من واجهة الفئة - ويحتوي كل كائن على مؤشر إلى جدول vtable الخاص بفئته، والذي يُستعان به عند استدعاء أي دالة متعددة الأشكال. تُعد هذه الآلية مثالًا على:

  • الربط المتأخر ، لأن استدعاءات الدوال الافتراضية لا يتم ربطها إلا في وقت الاستدعاء؛
  • الإرسال الفردي (أي تعدد الأشكال ذو الوسيط الواحد)، لأن استدعاءات الدوال الافتراضية مرتبطة ببساطة من خلال النظر في جدول الدوال الافتراضية الذي يوفره الوسيط الأول (الكائنthis)، لذا فإن أنواع وقت التشغيل للوسائط الأخرى غير ذات صلة تمامًا.

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

يؤدي التفاعل بين التعدد الشكلي البارامتري والتصنيف الفرعي إلى مفاهيم تباين النوع والقياس الكمي المحدود .

تعدد الأشكال الصفية

يُعدّ تعدد الأشكال الصفية [ 8 ] مفهومًا مشابهًا، ولكنه مختلف، عن التنميط الفرعي. وهو يتعامل مع الأنواع البنيوية . ويسمح باستخدام جميع القيم التي تمتلك أنواعها خصائص معينة، دون فقدان معلومات النوع المتبقية.

تعدد الأنماط

مفهوم ذو صلة هو تعدد الأنماط (أو عمومية أنواع البيانات ). الدالة متعددة الأنماط أكثر عمومية من الدالة متعددة الأشكال، وفي مثل هذه الدالة، "على الرغم من إمكانية توفير حالات مخصصة ثابتة لأنواع بيانات محددة، إلا أنه لا يوجد مُركِّب مخصص". [ 9 ]

تعدد الأشكال الرتبية

يُعد تعدد الأشكال الرتبي أحد السمات المميزة للغات برمجة المصفوفات ، مثل لغة APL . ويكمن جوهر نموذج البرمجة متعدد الأشكال الرتبي في التعامل الضمني مع جميع العمليات كعمليات تجميعية، قابلة للاستخدام على المصفوفات ذات الأبعاد المتعددة، [ 10 ] أي أن تعدد الأشكال الرتبي يسمح بتعريف الدوال للعمل على المصفوفات بأي شكل وحجم.

جوانب التنفيذ

التعدد الشكلي الثابت والديناميكي

يمكن تمييز تعدد الأشكال بحسب وقت اختيار التنفيذ: ثابتًا (أثناء الترجمة) أو ديناميكيًا (أثناء التشغيل، عادةً عبر دالة افتراضية ). يُعرف هذا على التوالي بالتوزيع الثابت والتوزيع الديناميكي ، وتُسمى الأشكال المقابلة لتعدد الأشكال بتعدد الأشكال الثابت وتعدد الأشكال الديناميكي .

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

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

عندما تُتاح خاصية تعدد الأشكال عبر مكتبة ، يصبح تعدد الأشكال الثابت مستحيلاً بالنسبة للمكتبات الديناميكية ، إذ لا توجد طريقة لمعرفة أنواع المعاملات عند إنشاء الكائن المشترك . في حين تستخدم لغات مثل C++ وRust قوالب أحادية الشكل ، تستخدم لغة البرمجة Swift على نطاق واسع الإرسال الديناميكي لإنشاء واجهة التطبيق الثنائية لهذه المكتبات افتراضيًا. ونتيجة لذلك، يمكن مشاركة المزيد من التعليمات البرمجية مع تقليل حجم النظام على حساب زيادة الحمل الزائد أثناء التشغيل. [ 11 ]

انظر أيضاً

مراجع

  1. 1 2 3 كارديلي، لوكا ؛ ويغنر، بيتر ( ديسمبر 1985). "حول فهم الأنواع، وتجريد البيانات، وتعدد الأشكال" ( ملف PDF) . مجلة ACM Computing Surveys . 17 (4): 471-523 . CiteSeerX 10.1.1.117.695 . doi : 10.1145/6041.6042 . S2CID 2921816. تعتمد لغات البرمجة التقليدية ذات الأنواع المحددة، مثل باسكال، على فكرة أن الدوال والإجراءات، وبالتالي معاملاتها، لها نوع فريد. يُقال إن هذه اللغات أحادية الشكل ، بمعنى أنه يمكن تفسير كل قيمة ومتغير على أنه من نوع واحد فقط. يمكن مقارنة لغات البرمجة أحادية الشكل بلغات البرمجة متعددة الأشكال التي قد يكون لبعض القيم والمتغيرات فيها أكثر من نوع واحد. الدوال متعددة الأشكال هي دوال يمكن أن يكون لمعاملاتها (المعلمات الفعلية) أكثر من نوع واحد. أما الأنواع متعددة الأشكال فهي أنواع يمكن تطبيق عملياتها على قيم من أكثر من نوع واحد.  
  2. ستروستروب، بيارن (19 فبراير 2007). "مسرد بيارن ستروستروب للغة سي++" . تعدد الأشكال - توفير واجهة واحدة لكيانات من أنواع مختلفة.
  3. "تعدد الأشكال" . دروس جافا: تعلم لغة جافا: الواجهات والوراثة . أوراكل . تم الاسترجاع في 8 سبتمبر 2021 .
  4. كونالين، ج.؛ إنجل، م.؛ هيوستن، ك.؛ ماكسيمتشوك، ر.؛ يونغ، ب.؛ بوتش، ج. (2007). تحليل وتصميم الكائنات مع التطبيقات ( الطبعة الثالثة). بيرسون للتعليم. ISBN  9780132797443.
  5. 1 2 ستراشي، كريستوفر (2000). "المفاهيم الأساسية في لغات البرمجة". الحوسبة الرمزية والحسابية من الرتبة العليا . 13 (1/2): 11-49 . CiteSeerX 10.1.1.332.3161 . doi : 10.1023/A:1010000313106 . ISSN 1573-0557 . S2CID 14124601 .   
  6. تاكر، ألين ب. (2004). دليل علوم الحاسوب ( الطبعة الثانية). تايلور وفرانسيس. ص 91–. ISBN   978-1-58488-360-9.
  7. بيرس، بي سي (2002). "23.2 أنواع تعدد الأشكال" . الأنواع ولغات البرمجة . مطبعة معهد ماساتشوستس للتكنولوجيا. ص 340-341 . ISBN  9780262162098.
  8. واند، ميتشل (يونيو 1989). "استدلال النوع لتسلسل السجلات والوراثة المتعددة". وقائع الندوة السنوية الرابعة حول المنطق في علوم الحاسوب . الصفحات 92-97 . doi : 10.1109/LICS.1989.39162 . 
  9. لاميل، رالف؛ فيسر، جوست (2002). "المُركِّبات المُنمَّطة للاجتياز العام". الجوانب العملية للغات التصريحية: الندوة الدولية الرابعة . سبرينغر. ص 137-154 ، انظر ص 153. CiteSeerX 10.1.1.18.5727 . ISBN   354043092X.
  10. سليباك، جاستن؛ شيفرز، أولين؛ مانوليوس، بانايوتيس (2019). "دلالات تعدد الأشكال الرتبية". arXiv : 1907.00509 [ cs.PL ].
  11. بينجيسنر، أليكسيس. "كيف حققت لغة سويفت الربط الديناميكي حيث لم تستطع لغة رست ذلك" .