البرمجة العامة

البرمجة العامة هي أسلوب برمجة حاسوبية تُكتب فيه الخوارزميات باستخدام أنواع بيانات تُحدد لاحقًا، ثم تُستدعى عند الحاجة لأنواع بيانات محددة تُقدم كمعاملات . هذا النهج، الذي طُوّر في لغة البرمجة ML عام 1973، [ 1 ] [ 2 ] يسمح بكتابة دوال أو أنواع بيانات مشتركة لا تختلف إلا في مجموعة أنواع البيانات التي تعمل عليها عند استخدامها، مما يقلل من تكرار التعليمات البرمجية .

تم تقديم البرمجة العامة إلى التيار السائد مع لغة آدا عام 1977. ومع استخدام القوالب في لغة سي++ ، أصبحت البرمجة العامة جزءًا من مجموعة أدوات تصميم المكتبات الاحترافية . وقد تم تحسين هذه التقنيات بشكل أكبر، وتم تقديم الأنواع المُعَلمة في كتاب أنماط التصميم المؤثر الصادر عام 1994. [ 3 ]

قدم أندريه ألكسندرسكو تقنيات جديدة في كتابه الصادر عام 2001 بعنوان " تصميم لغة C++ الحديثة : البرمجة العامة وتطبيق أنماط التصميم" . وفي وقت لاحق، طبقت لغة D نفس الأفكار.

تُعرف هذه الكيانات البرمجية باسم "الأنواع العامة" في لغات البرمجة Ada و C# و Delphi و Eiffel و F# و Java و Mojo و Nim و Python و Go و Rust و Swift و TypeScript و Visual Basic (.NET) . وتُعرف باسم "تعدد الأشكال البارامتري" في لغات البرمجة ML و Scala و Julia و Haskell . (يستخدم مصطلح Haskell أيضًا مصطلح "عام" لمفهوم ذي صلة ولكنه مختلف نوعًا ما).

صاغ ديفيد موسر وألكسندر ستيبانوف [ 4 ] مصطلح البرمجة العامة في الأصل بمعنى أكثر تحديدًا من المعنى المذكور أعلاه، لوصف نموذج برمجة يتم فيه استخلاص المتطلبات الأساسية لأنواع البيانات من أمثلة ملموسة للخوارزميات وهياكل البيانات وصياغتها كمفاهيم ، مع تنفيذ الوظائف العامة من حيث هذه المفاهيم، وعادةً ما يتم ذلك باستخدام آليات تعميم اللغة كما هو موضح أعلاه.

ستيبانوف-موسر ونماذج البرمجة العامة الأخرى

تم تعريف البرمجة العامة في موسر وستيبانوف (1989) على النحو التالي:

تتمحور البرمجة العامة حول فكرة التجريد من الخوارزميات الملموسة والفعالة للحصول على خوارزميات عامة يمكن دمجها مع تمثيلات بيانات مختلفة لإنتاج مجموعة واسعة من البرامج المفيدة.

موسر، ديفيد ر.؛ ستيبانوف، ألكسندر أ.، البرمجة العامة [ 5 ]

يُعدّ نموذج "البرمجة العامة" منهجًا لتجزئة البرمجيات، حيث تُستخلص المتطلبات الأساسية للأنواع من أمثلة ملموسة للخوارزميات وهياكل البيانات، وتُصاغ كمفاهيم ، على غرار تجريد النظريات الجبرية في الجبر المجرد . [ 6 ] طُبّقت أمثلة مبكرة لهذا المنهج البرمجي في لغتي Scheme وAda، [ 7 ] مع أن أشهر مثال هو مكتبة القوالب القياسية (STL)، [ 8 ] [ 9 ] التي طوّرت نظرية المُكرِّرات المستخدمة لفصل هياكل بيانات التسلسل عن الخوارزميات التي تعمل عليها.

على سبيل المثال، إذا توفرت لدينا N بنية بيانات متسلسلة، مثل قائمة مرتبطة أحادية أو متجه، و M خوارزمية للتعامل معها، فإنّ النهج المباشر يتطلب تنفيذ كل خوارزمية على حدة لكل بنية بيانات، مما ينتج عنه N × Mfind من التوليفات المطلوبة. أما في نهج البرمجة العامة، فتُعيد كل بنية بيانات نموذجًا لمفهوم المُكرِّر (نوع قيمة بسيط يُمكن الوصول إليه لاسترجاع القيمة الحالية، أو تغييره للإشارة إلى قيمة أخرى في التسلسل)، وتُكتب كل خوارزمية بشكل عام باستخدام وسائط هذه المُكرِّرات، مثل زوج من المُكرِّرات يُشير إلى بداية ونهاية التسلسل الفرعي أو النطاق المراد معالجته. وبالتالي، لا يلزم سوى تنفيذ N + M من توليفات بنية البيانات والخوارزمية. تُحدد مكتبة STL عدة مفاهيم للمكررات، كل منها عبارة عن تحسين لمفاهيم أكثر تقييدًا، فمثلاً، لا توفر المكررات الأمامية سوى الانتقال إلى القيمة التالية في التسلسل (وهي مناسبة مثلاً للقوائم المرتبطة أحادية الاتجاه أو تدفق بيانات الإدخال)، بينما يوفر المكرر ذو الوصول العشوائي أيضًا وصولاً مباشرًا وثابتًا زمنيًا إلى أي عنصر من عناصر التسلسل (وهو مناسب مثلاً للمتجهات). ومن المهم أن نلاحظ أن بنية البيانات تُعيد نموذجًا لأكثر المفاهيم عموميةً التي يمكن تنفيذها بكفاءة، حيث تُعد متطلبات التعقيد الحسابي جزءًا أساسيًا من تعريف المفهوم. وهذا يُقيد هياكل البيانات التي يمكن تطبيق خوارزمية معينة عليها، وتُعد متطلبات التعقيد هذه عاملاً حاسمًا في اختيار بنية البيانات. وبالمثل، طُبقت البرمجة العامة في مجالات أخرى، مثل خوارزميات الرسوم البيانية. [ 10 ]sort

على الرغم من أن هذا النهج يستخدم غالبًا ميزات اللغة المتعلقة بالتعميم في وقت الترجمة والقوالب، إلا أنه مستقل عن التفاصيل التقنية الخاصة باللغة. وقد كتب رائد البرمجة العامة ألكسندر ستيبانوف:

تُعنى البرمجة العامة بتجريد وتصنيف الخوارزميات وهياكل البيانات. وهي مستوحاة من كنوت ، لا من نظرية الأنواع. هدفها هو البناء التدريجي لفهارس منهجية للخوارزميات وهياكل البيانات المفيدة والفعالة والمجردة. ولا يزال هذا المسعى حلماً بعيد المنال.

ألكسندر ستيبانوف، تاريخ موجز لـ STL [ 11 ] [ 12 ]

أعتقد أن نظريات التكرار أساسية في علوم الحاسوب بقدر ما تعتبر نظريات الحلقات أو فضاءات باناخ أساسية في الرياضيات.

ألكسندر ستيبانوف، مقابلة مع أ. ستيبانوف [ 13 ]

وأشار بيارن ستروستروب إلى ذلك.

باتباع ستيبانوف، يمكننا تعريف البرمجة العامة دون ذكر ميزات اللغة: رفع الخوارزميات وهياكل البيانات من أمثلة ملموسة إلى شكلها الأكثر عمومية وتجريدًا.

بيارن ستروستروب، تطوير لغة في العالم الحقيقي ومن أجله: C++ 1991-2006 [ 12 ]

تشمل نماذج البرمجة الأخرى التي وُصفت بالبرمجة العامة برمجة أنواع البيانات العامة كما هو موضح في "البرمجة العامة - مقدمة". [ 14 ] يُعدّ أسلوب "التخلص من الشيفرة النمطية " أسلوبًا خفيفًا للبرمجة العامة في لغة هاسكل. [ 15 ]

في هذه المقالة، نميز بين نماذج البرمجة عالية المستوى للبرمجة العامة ، المذكورة أعلاه، وآليات التعميم في لغات البرمجة منخفضة المستوى المستخدمة لتنفيذها (انظر دعم لغات البرمجة للتعميم ). لمزيد من المناقشة والمقارنة بين نماذج البرمجة العامة، انظر [ 16 ] .

دعم لغات البرمجة للتعميم

لقد كانت إمكانيات التعميم موجودة في لغات البرمجة عالية المستوى منذ سبعينيات القرن الماضي على الأقل في لغات مثل ML و CLU و Ada ، وتم اعتمادها لاحقًا من قبل العديد من اللغات القائمة على الكائنات والموجهة للكائنات ، بما في ذلك BETA و C++ و D و Eiffel و Java و Trellis-Owl التابعة لشركة DEC والتي لم تعد موجودة الآن .

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

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

فيما يلي استعراض شامل لآليات البرمجة العامة في لغات البرمجة. للاطلاع على استعراض محدد يقارن مدى ملاءمة الآليات للبرمجة العامة، انظر [ 17 ] .

في لغات البرمجة الكائنية التوجه

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

class Animal { // ... };class Car { // ... };template < typename T > class MyList { // محتويات الفئة. };MyList < الحيوان > قائمة الحيوانات ؛ MyList < سيارة > قائمة السيارات ؛

أعلاه، Tيوجد عنصر نائب لأي نوع يتم تحديده عند إنشاء القائمة. تسمح هذه "الكائنات العامة من النوع T"، والتي تُسمى عادةً بالقوالب ، بإعادة استخدام فئة مع أنواع بيانات مختلفة طالما تم الحفاظ على بعض الشروط مثل الأنواع الفرعية والتوقيع . يجب عدم الخلط بين آلية العمومية هذه وتعدد الأشكال التضميني ، وهو الاستخدام الخوارزمي للفئات الفرعية القابلة للتبديل: على سبيل المثال، قائمة من كائنات من النوع MovingEntityتحتوي على كائنات من النوعين Animalو Car. يمكن أيضًا استخدام القوالب للوظائف المستقلة عن النوع كما في مثال التبديل أدناه:

استيراد std ؛باستخدام std :: string ;// دالة مشابهة، ولكنها أكثر أمانًا وربما أسرع ، مُعرَّفة في ملف رأس المكتبة القياسية <utility> template < typename T > void swap ( T & a , T & b ) noexcept { T temp = b ; b = a ; a = temp ; }int main ( int argc , char * argv []) { string world = "World!" ; string hello = "Hello," ; swap ( world , hello ); std :: println ( "{} {}" , world , hello ); // الناتج هو "Hello, World!". }

templateيُعتبر بناء لغة C++ المذكور أعلاه مثالًا شائعًا على بناء البرمجة العامة الذي ساهم في انتشار هذا المفهوم بين المبرمجين ومصممي اللغات ، وهو يدعم العديد من أساليب البرمجة العامة. كما توفر لغة D قوالب قابلة للبرمجة العامة بالكامل، مستندةً إلى نموذج C++ ولكن بصيغة مبسطة. أما لغة Java، فقد وفرت إمكانيات البرمجة العامة، استنادًا إلى بنية C++، منذ إصدار Java Platform, Standard Edition (J2SE) 5.0.

تحتوي لغات البرمجة C# 2.0 و Oxygene 1.5 (المعروفة سابقًا باسم Chrome) و Visual Basic (.NET) 2005 على بنيات تستغل دعم الأنواع العامة الموجودة في Microsoft .NET Framework منذ الإصدار 2.0.

آدا

تدعم لغة آدا البرمجة العامة منذ تصميمها الأول في الفترة ما بين عامي 1977 و1980. وتستخدم المكتبة القياسية البرمجة العامة لتوفير العديد من الخدمات. أضافت آدا 2005 مكتبة حاويات عامة شاملة إلى المكتبة القياسية، مستوحاة من مكتبة القوالب القياسية للغة سي++ . [ 18 ] [ 19 ]

الوحدة العامة هي حزمة أو برنامج فرعي يأخذ معلمة رسمية عامة واحدة أو أكثر . [ 20 ]

المعامل الرسمي العام هو قيمة، أو متغير، أو ثابت، أو نوع، أو برنامج فرعي، أو حتى نسخة من وحدة عامة أخرى محددة. بالنسبة للأنواع الرسمية العامة، يميز بناء الجملة بين الأنواع المنفصلة، ​​وأنواع الفاصلة العائمة، وأنواع الفاصلة الثابتة، وأنواع الوصول (المؤشرات)، وما إلى ذلك. يمكن أن تحتوي بعض المعاملات الرسمية على قيم افتراضية. [ 21 ]

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

مثال

مواصفات حزمة عامة:

generic Max_Size : Natural ; -- نوع قيمة رسمي عام Element_Type is private ; -- نوع رسمي عام؛ يقبل أي نوع غير محدود package Stacks is type Size_Type is range 0 .. Max_Size ; type Stack is limited private ; procedure Create ( S : out Stack ; Initial_Size : in Size_Type := Max_Size ); procedure Push ( Into : in out Stack ; Element : in Element_Type ); procedure Pop ( From : in out Stack ; Element : out Element_Type ); Overflow : exception ; Underflow : exception ; private subtype Index_Type is Size_Type range 1 .. Max_Size ; type Vector is array ( Index_Type range <>) of Element_Type ; type Stack ( Allocated_Size : Size_Type := 0 ) is record Top : Index_Type ; Storage : Vector ( 1 .. Allocated_Size ); end record ; end Stacks ;

إنشاء نسخة من الحزمة العامة:

نوع Bookmark_Type هو نوع Natural جديد ؛ -- يسجل موقعًا في مستند النص الذي نقوم بتحريرهالحزمة Bookmark_Stacks هي كائن جديد من نوع Stacks ( الحد الأقصى للحجم => 20 ، نوع العنصر => Bookmark_Type -- يسمح للمستخدم بالانتقال بين المواقع المسجلة في المستند

استخدام نسخة من حزمة عامة:

نوع Document_Type هو سجل Contents : Ada . Strings . Unbounded . Unbounded_String ; Bookmarks : Bookmark_Stacks . Stack ; نهاية السجل ;الإجراء Edit ( اسم_المستند : سلسلة نصية ) هو مستند : نوع_المستند ؛ ابدأ -- تهيئة مجموعة الإشارات المرجعية: Bookmark_Stacks.Create ( S = > Document.Bookmarks , Initial_Size = > 10 ) ؛ -- الآن، افتح الملف اسم_المستند واقرأه... نهاية Edit ؛
المزايا والعيوب

تتيح بنية اللغة تحديدًا دقيقًا للقيود المفروضة على المعاملات الرسمية العامة. على سبيل المثال، من الممكن تحديد أن نوعًا رسميًا عامًا لن يقبل إلا نوعًا معياريًا كقيمة فعلية. كما يمكن التعبير عن القيود بين المعاملات الرسمية العامة؛ على سبيل المثال:

النوع العام Index_Type هو (<>)؛ -- يجب أن يكون نوعًا منفصلاً. النوع Element_Type خاص ؛ -- يمكن أن يكون أي نوع غير محدود. النوع Array_Type هو مصفوفة ( نطاق Index_Type <>) من Element_Type ؛

في هذا المثال، Array_Typeيكون مقيدًا بكل من Index_Typeو Element_Type. عند إنشاء الوحدة، يجب على المبرمج تمرير نوع مصفوفة فعلي يفي بهذه القيود.

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

على عكس لغة C++، لا تسمح لغة Ada بإنشاء نسخ عامة متخصصة، وتتطلب إنشاء جميع الأنواع العامة بشكل صريح. لهذه القواعد عدة نتائج:

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

لغة سي++

تستخدم لغة C++ القوالب لتمكين تقنيات البرمجة العامة. تتضمن مكتبة C++ القياسية مكتبة القوالب القياسية (STL) التي توفر إطار عمل من القوالب لهياكل البيانات والخوارزميات الشائعة. يمكن أيضًا استخدام القوالب في C++ لبرمجة القوالب الوصفية ، وهي طريقة لتقييم بعض أجزاء الكود مسبقًا في وقت الترجمة بدلًا من وقت التشغيل . باستخدام تخصيص القوالب، تُعتبر قوالب C++ كاملة تورينج .

نظرة عامة فنية

توجد أنواع عديدة من القوالب، وأكثرها شيوعًا قوالب الدوال وقوالب الأصناف. قالب الدالة هو نمط لإنشاء دوال عادية بناءً على أنواع المعاملات المُقدمة عند إنشاء الكائن. على سبيل المثال، تحتوي مكتبة قوالب C++ القياسية على قالب الدالة std::max(x, y)الذي يُنشئ دوالًا تُرجع إما `true` xأو y`false`، أيهما أكبر. max()يمكن تعريف `false` على النحو التالي:

template < typename T > [[ nodiscard ]] constexpr T max ( T x , T y ) noexcept { return x < y ? y : x ; }

يمكن استدعاء التخصصات الخاصة بقالب هذه الدالة، أي النسخ ذات الأنواع المحددة، تمامًا مثل الدالة العادية:

std :: println ( "{}" , max ( 3 , 7 )); // الناتج 7.

يقوم المترجم بفحص الوسائط المستخدمة لاستدعاء الدالة maxويحدد أنها استدعاء للدالة max(int, int). ثم يقوم بإنشاء نسخة من الدالة حيث يكون نوع المتغيرT هو int، مما يجعل الدالة مكافئة للدالة التالية:

[[ nodiscard ]] constexpr int max ( int ​​x , int y ) noexcept { return x < y ? y : x ; }

يعمل هذا سواء كانت الوسائط xأعدادًا yصحيحة أو سلاسل نصية أو أي نوع آخر يكون التعبير x < yمنطقيًا معه، أو بشكل أكثر تحديدًا، لأي نوع operator<مُعرَّف له. لا حاجة إلى الوراثة المشتركة لمجموعة الأنواع التي يمكن استخدامها، لذا فهو مشابه جدًا للتصنيف الديناميكي . يمكن لبرنامج يُعرِّف نوع بيانات مخصص استخدام تحميل المعاملات الزائد لتحديد معنى <هذا النوع، مما يسمح باستخدامه مع std::max()قالب الدالة. مع أن هذه الميزة قد تبدو بسيطة في هذا المثال المنفرد، إلا أنها في سياق مكتبة شاملة مثل STL، تُمكِّن المبرمج من الحصول على وظائف واسعة لنوع بيانات جديد، بمجرد تعريف عدد قليل من المعاملات له. <يسمح تعريف هذا النوع باستخدامه مع الخوارزميات القياسية ، std::sort()أو وضعه داخل هياكل بيانات مثل (المكافئة لـ ، والأكوام ، والمصفوفات الترابطية ).std::stable_sort()std::binary_search()std::set<T>TreeSet<T>

تُعتبر قوالب C++ آمنة تمامًا من حيث النوع أثناء الترجمة. على سبيل المثال، std::complex<T>لا يُعرّف النوع القياسي <عامل المقارنة، لعدم وجود ترتيب صارم للأعداد المركبة . لذلك، std::max(x, y)سيفشل التطبيق مع ظهور خطأ في الترجمة إذا كانت ` xa` و` yb` قيمتين. وبالمثل، لا يمكن تطبيق complexالقوالب الأخرى التي تعتمد على `a` على البيانات إلا إذا تم توفير مقارنة (في شكل دالة أو مُعامل). على سبيل المثال، لا يمكن استخدام `a` كمفتاح لـ `a` (المكافئ لـ `a` ) إلا إذا تم توفير مقارنة. لسوء الحظ، تُولّد المُترجمات تاريخيًا رسائل خطأ غامضة وطويلة وغير مفيدة لهذا النوع من الأخطاء. يمكن التخفيف من هذه المشكلة بضمان التزام كائن معين ببروتوكول طريقة . كما يمكن للغات التي تستخدم `a` بدلاً من `b` استخدام القيم كمفاتيح.<complexstd::complex<T>std::map<K, V>TreeMap<K, V>std::compare<std::complex<T>

نوع آخر من القوالب، وهو قالب الفئة ، يوسع المفهوم نفسه ليشمل الفئات. يُعد تخصيص قالب الفئة فئةً بحد ذاته. تُستخدم قوالب الفئات غالبًا لإنشاء حاويات عامة. على سبيل المثال، تحتوي مكتبة STL على حاوية قائمة مرتبطة ثنائيةstd::list<T> (مكافئة لـ LinkedList<T>). لإنشاء قائمة مرتبطة من الأعداد الصحيحة، نكتب list<int>، بينما يُرمز لقائمة من السلاسل النصية بـ list<string>. std::list<T>تحتوي على مجموعة من الدوال القياسية المرتبطة بها، والتي تعمل مع أي أنواع مُعاملة متوافقة.

يُقدّم C++20 مفهوم تقييد أنواع القوالب باستخدام المفاهيم . max()قد يبدو تقييد الأنواع كالتالي:

استيراد std ؛باستخدام std :: total_ordered ;// في تعريف اسم النوع: template < totally_ordered T > [[ nodiscard ]] constexpr T max ( T x , T y ) noexcept { return x < y ? y : x ; }// في عبارة requires: template < typename T > requires totally_ordered < T > [[ nodiscard ]] constexpr T max ( T x , T y ) noexcept { return x < y ? y : x ; }
تخصص القالب

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

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

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

المزايا والعيوب

كانت بعض استخدامات القوالب، مثل max()الدالة، تُؤدّى سابقًا بواسطة وحدات ماكرو للمعالجة المسبقة تُشبه الدوال (إرث من لغة C ). على سبيل المثال، إليك تطبيقًا مُحتملًا لمثل هذه الوحدة:

#define max(a,b) ((a) < (b) ? (b) : (a))

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

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

هناك أربعة عيوب رئيسية لاستخدام القوالب: الميزات المدعومة، ودعم المترجم، ورسائل الخطأ الضعيفة (عادةً مع ما قبل C++20، حيث يكون فشل الاستبدال ليس خطأ ( SFINAE ))، وتضخم الكود :

  1. تفتقر القوالب في لغة C++ إلى العديد من الميزات، مما يجعل تنفيذها واستخدامها بطريقة مباشرة أمرًا شبه مستحيل في كثير من الأحيان. وبدلًا من ذلك، يضطر المبرمجون إلى الاعتماد على حيل معقدة، مما يؤدي إلى شيفرة برمجية ضخمة يصعب فهمها وصيانتها. وتزيد التطورات الحالية في معايير C++ من تفاقم هذه المشكلة من خلال الإفراط في استخدام هذه الحيل، وبناء العديد من الميزات الجديدة للقوالب بالاعتماد عليها أو بالتزامن معها.
  2. لطالما افتقرت العديد من المترجمات إلى دعمٍ كافٍ للقوالب، مما قد يجعل استخدامها أقل قابليةً للنقل بين البرامج. وقد يكون الدعم ضعيفًا أيضًا عند استخدام مترجم C++ مع رابط لا يدعم C++، أو عند محاولة استخدام القوالب عبر حدود المكتبات المشتركة .
  3. قد تُنتج المترجمات رسائل خطأ مُربكة وطويلة، وأحيانًا غير مفيدة، عند اكتشاف أخطاء في التعليمات البرمجية التي تستخدم SFINAE. [ 22 ] وهذا قد يجعل تطوير القوالب أمرًا صعبًا.
  4. أخيرًا، يتطلب استخدام القوالب من المُصرّف إنشاء نسخة منفصلة من الفئة أو الدالة المُنمّطة لكل مُعامل نوع مُستخدم معها. (هذا ضروري لأن أنواع البيانات في لغة C++ ليست جميعها بنفس الحجم، وأحجام حقول البيانات مهمة لكيفية عمل الفئات). لذا، فإن الاستخدام العشوائي للقوالب قد يؤدي إلى تضخم حجم الكود ، مما ينتج عنه ملفات تنفيذية كبيرة جدًا. مع ذلك، فإن الاستخدام المُحكم لتخصيص القوالب واشتقاقها يُمكن أن يُقلل بشكل كبير من هذا التضخم في بعض الحالات.

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

    بيارن ستروستروب ، تصميم وتطور لغة C++، 1994 [ 23 ]

  5. قد تتطلب الفئات أو الدوال ذات القوالب تخصيصًا صريحًا لفئة القالب، الأمر الذي يتطلب إعادة كتابة فئة كاملة لمعلمات قالب محددة تستخدمها.

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

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

د

تدعم لغة D القوالب المصممة على أساس لغة C++. تعمل معظم أنماط قوالب C++ في D دون تغيير، لكن D تضيف بعض الوظائف:

  • لا تقتصر معلمات القوالب في D على الأنواع والقيم الأولية فقط (كما كان الحال في C++ قبل C++20)، ولكنها تسمح أيضًا بقيم وقت الترجمة العشوائية (مثل السلاسل والقيم الحرفية للبنية)، والأسماء المستعارة للمعرفات العشوائية، بما في ذلك القوالب الأخرى أو إنشاءات القوالب.
  • توفر قيود القوالب والبيان static ifبديلاً لمفاهيم C++ و على التوالي if constexpr.
  • يسمح هذا is(...)التعبير بالإنشاء التخميني للتحقق من سمات الكائن في وقت الترجمة.
  • تسمح الكلمة المفتاحية والتعبير باستنتاج autoالنوع لتصريحات المتغيرات وقيم الإرجاع للدوال، مما يسمح بدوره بـ "أنواع فولدمورت" (الأنواع التي ليس لها اسم عام). [ 24 ]typeof

تستخدم القوالب في لغة D صيغةً مختلفةً عن تلك المستخدمة في لغة C++: فبينما تُغلّف معاملات القوالب في C++ بأقواس زاوية ( Template<param1, param2>)، تستخدم D علامة تعجب وأقواسًا عادية: Template!(param1, param2). وهذا يُجنّبنا صعوبات تحليل C++ الناتجة عن الغموض في عوامل المقارنة. في حال وجود معامل واحد فقط، يُمكن حذف الأقواس.

تقليديًا، تجمع لغة D بين الميزات المذكورة أعلاه لتوفير تعدد الأشكال في وقت الترجمة باستخدام البرمجة العامة القائمة على السمات. على سبيل المثال، يُعرَّف نطاقisInputRange الإدخال بأنه أي نوع يستوفي الشروط التي تُجريها الدالة ، والتي تُعرَّف على النحو التالي:

template isInputRange ( R ) { enum bool isInputRange = is ( typeof (( inout int = 0 ) { R r = R.init ; // يمكن تعريف كائن نطاق if ( r.empty ) {} // يمكن التحقق من كونه فارغًا r.popFront ( ) ; // يمكن استدعاء popFront () auto h = r.front ; // يمكن الحصول على بداية النطاق } )); }

يمكن للدالة التي تقبل نطاقات الإدخال فقط استخدام القالب المذكور أعلاه في قيد القالب:

دالة تلقائية ( النطاق ) ( النطاق النطاق ) إذا ( كان نطاق الإدخال ! النطاق ) { // ... }
توليد الكود

بالإضافة إلى برمجة القوالب الوصفية، توفر لغة D أيضًا العديد من الميزات لتمكين توليد التعليمات البرمجية في وقت الترجمة:

  • يسمح هذا importالتعبير بقراءة ملف من القرص واستخدام محتوياته كتعبير نصي.
  • تتيح خاصية الانعكاس في وقت الترجمة إمكانية تعداد وفحص التصريحات وأعضائها أثناء عملية الترجمة.
  • تسمح السمات التي يحددها المستخدم للمستخدمين بإرفاق معرفات عشوائية بالتصريحات، والتي يمكن بعد ذلك تعدادها باستخدام انعكاس وقت الترجمة.
  • يسمح تنفيذ الوظائف في وقت الترجمة (CTFE) بتفسير مجموعة فرعية من D (مقتصرة على العمليات الآمنة) أثناء الترجمة.
  • تتيح ميزة String mixins تقييم وتجميع محتويات تعبير السلسلة كرمز D يصبح جزءًا من البرنامج.

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

يُتيح التعبير importوتنفيذ الدوال في وقت الترجمة أيضًا تنفيذ لغات خاصة بالمجال بكفاءة . على سبيل المثال، إذا كانت لدينا دالة تأخذ سلسلة نصية تحتوي على قالب HTML وتعيد شفرة مصدرية مكافئة بلغة D، فيمكن استخدامها بالطريقة التالية:

// استورد محتويات الملف example.htt كسلسلة نصية ثابتة. enum htmlTemplate = import ( "example.htt" );// نقل قالب HTML إلى كود D. تعداد htmlDCode = htmlTemplateToD ( htmlTemplate );// الصق محتويات htmlDCode كرمز D. mixin ( htmlDCode );

إيفل

تُعدّ الفئات العامة جزءًا من لغة إيفل منذ تصميمها الأصلي. وتستخدم المنشورات الأساسية لإيفل [ 25 ] [ 26 ] مصطلح "العمومية" لوصف إنشاء الفئات العامة واستخدامها.

عامة أساسية وغير مقيدة

تُعلن الفئات العامة باسمها وقائمة تضم مُعاملًا عامًا رسميًا واحدًا أو أكثر . في الكود التالي، LISTتحتوي الفئة على مُعامل عام رسمي واحد.G

class LIST [ G ] ... feature -- الوصول إلى العنصر : G -- العنصر الذي يشير إليه المؤشر حاليًا ... feature -- تغيير العنصر put ( new_item : G ) -- إضافة `new_item` في نهاية القائمة ...

تُعدّ المعاملات العامة الرسمية عناصر نائبة لأسماء فئات عشوائية سيتم توفيرها عند تعريف الفئة العامة، كما هو موضح في الاشتقاقين العامين أدناه، حيث يمثل ACCOUNTكل من DEPOSITو أسماء فئات أخرى. وتُعتبر ACCOUNTو معاملات عامة فعلية لأنها توفر أسماء فئات حقيقية يمكن استبدالها بـ في الاستخدام الفعلي.DEPOSITG

قائمة_الحسابات : قائمة [ الحساب ] -- قائمة الحساباتقائمة_الودائع : قائمة [ الودائع ] -- قائمة الودائع

في نظام أنواع إيفل، على الرغم من أن الصنف LIST [G]يُعتبر صنفًا، إلا أنه لا يُعتبر نوعًا. ومع ذلك، فإن الاشتقاق العام منه LIST [G]يُعتبر LIST [ACCOUNT]نوعًا.

العمومية المقيدة

بالنسبة لفئة القائمة الموضحة أعلاه، Gيمكن أن يكون أي صنف آخر متاح بديلاً عن المعامل العام الفعلي. لتقييد مجموعة الأصناف التي يمكن اختيار المعاملات العامة الفعلية الصالحة منها، يمكن تحديد قيد عام . في تعريف الصنف SORTED_LISTأدناه، ينص القيد العام على أن أي معامل عام فعلي صالح سيكون صنفًا يرث من الصنف COMPARABLE. يضمن القيد العام SORTED_LISTإمكانية فرز عناصر القائمة.

class SORTED_LIST [ G -> COMPARABLE ]

جافا

أُضيف دعم الأنواع العامة ، أو "حاويات النوع T"، إلى لغة برمجة جافا في عام 2004 كجزء من J2SE 5.0. في جافا، لا يتم التحقق من صحة الأنواع العامة إلا أثناء الترجمة. ثم تُزال معلومات النوع العام عبر عملية تُسمى " محو النوع" ، للحفاظ على التوافق مع تطبيقات JVM القديمة ، مما يجعلها غير متاحة أثناء التشغيل. [ 27 ] على سبيل المثال، List<String>يتم تحويل a إلى النوع الخام java.util.List. يُدرج المُترجم تحويلات النوع لتحويل العناصر إلى java.lang.Stringالنوع عند استرجاعها من القائمة، مما يُقلل الأداء مقارنةً بتطبيقات أخرى مثل قوالب C++.

.شبكة

أُضيفت الأنواع العامة كجزء من إطار عمل .NET 2.0 في نوفمبر 2005، استنادًا إلى نموذج أولي بحثي من مايكروسوفت للأبحاث بدأ عام 1999. [ 28 ] على الرغم من تشابهها مع الأنواع العامة في جافا، فإن الأنواع العامة في .NET لا تُطبّق حذف النوع ، [ 29 ] : 208-209 ، بل تُنفّذ الأنواع العامة كآلية أساسية في وقت التشغيل باستخدام التجسيد . يوفر هذا الخيار التصميمي وظائف إضافية، مثل السماح بالانعكاس مع الحفاظ على الأنواع العامة، وتخفيف بعض قيود الحذف (مثل عدم القدرة على إنشاء مصفوفات عامة). [ 30 ] [ 31 ] هذا يعني أيضًا عدم وجود تأثير سلبي على الأداء ناتج عن عمليات التحويل في وقت التشغيل وتحويلات التغليف المكلفة عادةً . عند استخدام الأنواع الأولية وأنواع القيم كوسائط عامة، فإنها تحصل على تطبيقات متخصصة، مما يسمح بإنشاء مجموعات وأساليب عامة فعّالة. كما هو الحال في لغتي C++ و Java، فإن الأنواع العامة المتداخلة مثل Dictionary<string, List<int>>الأنواع الصالحة، ولكن يُنصح بتجنبها في توقيعات الأعضاء في قواعد تصميم تحليل التعليمات البرمجية. [ 32 ]

تتيح بيئة .NET ستة أنواع من قيود الأنواع العامة باستخدام الكلمة whereالمفتاحية، بما في ذلك تقييد الأنواع العامة لتكون أنواع قيم، أو فئات، أو أن تحتوي على دوال إنشاء، أو أن تُنفذ واجهات. [ 33 ] فيما يلي مثال على قيد واجهة:

namespace Wikipedia.Examples ;باستخدام النظام ؛public class Example { static void Main () { int [] a = { 0 , 1 , 2 , 3 }; MakeAtLeast < int > ( a , 2 ); // تغيير a إلى { 2, 2, 2, 3 } foreach ( int i in a ) { Console . WriteLine ( i ); // طباعة النتائج. } Console . ReadKey ( true ); }static void MakeAtLeast <T> ( T [ ] list , T lowest ) where T : IComparable <T> { for ( int i = 0 ; i < list.Length ; i ++ ) { if ( list [ i ] .CompareTo ( lowest ) < 0 ) { list [ i ] = lowest ; } } } }

تتيح هذه MakeAtLeast()الطريقة إجراء عمليات على المصفوفات، بعناصر من نوع عام T. يشير قيد النوع الخاص بالطريقة إلى أنها قابلة للتطبيق على أي نوع Tيُنفذ System.IComparable<T>الواجهة العامة. وهذا يضمن حدوث خطأ أثناء الترجمة إذا تم استدعاء الطريقة في حال كان النوع لا يدعم المقارنة. توفر الواجهة الطريقة العامة CompareTo(T).

يمكن كتابة الطريقة المذكورة أعلاه بدون استخدام الأنواع العامة، وذلك ببساطة باستخدام System.Arrayالنوع غير العام. مع ذلك، ولأن المصفوفات متغيرة عكسيًا ، فإن عملية التحويل لن تكون آمنة من ناحية النوع ، ولن يتمكن المترجم من اكتشاف بعض الأخطاء المحتملة التي كان من الممكن اكتشافها عند استخدام الأنواع العامة. إضافةً إلى ذلك، ستحتاج الطريقة إلى الوصول إلى عناصر المصفوفة كـ objects، وستتطلب تحويلًا لمقارنة عنصرين. (بالنسبة لأنواع القيم مثل intهذه، يتطلب الأمر تحويلًا باستخدام التغليف ، مع إمكانية تجاوز ذلك باستخدام System.Collections.Generic.Comparer<T>الفئة، كما هو الحال في فئات المجموعات القياسية).

من السلوكيات الملحوظة للأعضاء الثابتة في فئة .NET العامة هو إنشاء الأعضاء الثابتة لكل نوع وقت التشغيل (انظر المثال أدناه).

namespace Wikipedia.Examples ;باستخدام النظام ؛// فئة عامة class GenericTest <T> { // متغير ثابت - سيتم إنشاؤه لكل نوع عند استخدام الانعكاس static CountedInstances OnePerType = new ( );// عضو بيانات خاص T _t ;// المُنشئ الافتراضي public GenericTest ( T t ) { _t = t ; } }// فئة ` CountedInstances` { // متغير ثابت - سيتم زيادته مرة واحدة لكل مثيل ` public static int Counter ;`// المُنشئ الافتراضي public CountedInstances () { // زيادة العداد بمقدار واحد أثناء إنشاء الكائن CountedInstances . Counter ++ ; } }public class GenericExample { static void Main ( string [] args ) { // نقطة دخول الكود الرئيسي // في نهاية التنفيذ، CountedInstances.Counter = 2 GenericTest < int > g1 = new ( 1 ); GenericTest < int > g11 = new ( 11 ); GenericTest < int > g111 = new ( 111 ); GenericTest < double > g2 = new ( 1.0 ); } }

باسكال

بالنسبة للغة باسكال ، تم تطبيق البرمجة العامة لأول مرة في عام 2006، في تطبيق Free Pascal .

دلفي

اكتسبت لغة دلفي، وهي لهجة من لغة أوبجكت باسكال، ميزة الأنواع العامة في إصدار دلفي 11 عام 2007 من شركة كود جير ، وكانت هذه الميزة متاحة في البداية فقط مع مُصرّف .NET (الذي توقف دعمه لاحقًا)، قبل إضافتها إلى الكود الأصلي في إصدار دلفي 12 عام 2009. تُحاكي دلالات وقدرات الأنواع العامة في دلفي إلى حد كبير دلالات وقدرات الأنواع العامة في .NET 2.0، على الرغم من أن التنفيذ يختلف بالضرورة اختلافًا كبيرًا. إليك ترجمة شبه مباشرة للمثال الأول المكتوب بلغة C# والموضح أعلاه:

نموذج البرنامج ؛{$APPTYPE CONSOLE}يستخدم Generics.Defaults ؛ // لـ IComparer <>نوع TUtils = فئة إجراء الفئة MakeAtLeast <T> ( Arr : TArray <T> ; const Lowest : T ; Comparer : IComparer <T> ) ; تحميل زائد ; إجراء الفئة MakeAtLeast <T> ( Arr : TArray <T> ; const Lowest : T ) ; تحميل زائد ; نهاية ;إجراء الفئة TUtils.MakeAtLeast <T> ( Arr : TArray <T> ; const Lowest : T ; Comparer : IComparer <T> ) ; var I : Integer ; begin if Comparer = nil then Comparer : = TComparer <T> .Default ; for I : = Low ( Arr ) to High ( Arr ) do if Comparer.Compare ( Arr [ I ] , Lowest ) < 0 then Arr [ I ] : = Lowest ; end ;إجراء الفئة TUtils.MakeAtLeast <T> ( Arr : TArray <T> ; const Lowest : T ) ; begin MakeAtLeast <T> ( Arr , Lowest , nil ) ; end ;var Ints : TArray < Integer >; Value : Integer ; begin Ints := TArray < Integer >. Create ( 0 , 1 , 2 , 3 ) ; TUtils . MakeAtLeast < Integer > ( Ints , 2 ) ; for Value in Ints do WriteLn ( Value ) ; ReadLn ; end .

كما هو الحال في لغة C#، يمكن أن تحتوي الدوال والأنواع الكاملة على مُعامل نوع واحد أو أكثر. في المثال، TArray هو نوع عام (مُعرّف في اللغة)، وMakeAtLeast دالة عامة. القيود المتاحة مشابهة جدًا للقيود المتاحة في C#: أي نوع قيمة، أي فئة، فئة أو واجهة مُحددة، وفئة ذات مُنشئ بدون مُعاملات. تعمل القيود المتعددة كاتحاد جمعي.

فري باسكال

طبّقت لغة فري باسكال البرمجة العامة في عام 2006 في الإصدار 2.2.0 ، قبل لغة دلفي وبصيغة ودلالات مختلفة. مع ذلك، منذ الإصدار 2.6.0 من فري باسكال، أصبحت صيغة دلفي متاحة عند استخدام وضع اللغة {$mode Delphi}. بالتالي، يدعم كود فري باسكال البرمجة العامة بكلا النمطين.

مثال باستخدام دلفي وفري باسكال:

// وحدة نمط دلفي A ;{$ifdef fpc} {$mode delphi} {$endif}واجهة المستخدمtype TGenericClass <T> = class function Foo ( const AValue : T ) : T ; end ;تطبيقالدالة TGenericClass <T> .Foo ( قيمة ثابتة : T ) : T ; نتيجة البداية := AValue + AValue ; نهاية ؛نهاية .// وحدة نمط ObjFPC في لغة Free Pascal، الوحدة B ؛{$ifdef fpc} {$mode objfpc} {$endif}واجهة المستخدمtype generic TGenericClass < T > = class function Foo ( const AValue : T ) : T ; end ;تطبيقالدالة TGenericClass . Foo ( قيمة ثابتة : T ) : T ; نتيجة البداية := AValue + AValue ; نهاية ؛نهاية .// مثال للاستخدام، برنامج نمط دلفي TestGenDelphi ؛{$ifdef fpc} {$mode delphi} {$endif}يستخدم A و B ؛var GC1 : A . TGenericClass < Integer >; GC2 : B . TGenericClass < String >; begin GC1 := A . TGenericClass < Integer >. Create ; GC2 := B . TGenericClass < String >. Create ; WriteLn ( GC1 . Foo ( 100 )) ; // 200 WriteLn ( GC2 . Foo ( 'hello' )) ; // hellohello GC1 . Free ; GC2 . Free ; end .// مثال للاستخدام، برنامج نمط ObjFPC TestGenDelphi ؛{$ifdef fpc} {$mode objfpc} {$endif}يستخدم A و B ؛// مطلوب في ObjFPC نوع TAGenericClassInt = specialize A . TGenericClass < Integer >; TBGenericClassString = specialize B . TGenericClass < String >; var GC1 : TAGenericClassInt ; GC2 : TBGenericClassString ; begin GC1 := TAGenericClassInt . Create ; GC2 := TBGenericClassString . Create ; WriteLn ( GC1 . Foo ( 100 )) ; // 200 WriteLn ( GC2 . Foo ( 'hello' )) ; // hellohello GC1 . Free ; GC2 . Free ; end .

اللغات الوظيفية

التعميم في لغة هاسكل

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

data BinTree a = Leaf a | Node ( BinTree a ) a ( BinTree a ) deriving ( Eq , Show )

ينتج عن ذلك تعريف دالة المساواة ( ==) ودالة تمثيل السلسلة ( show) تلقائيًا لأي نوع من أنواع النموذج BinTree Tالذي Tيدعم هذه العمليات.

إن دعم الحالات المشتقة من الدوال يجعل أساليبها ووظائفها Eqعامة بطريقة مختلفة نوعيًا عن الدوال متعددة الأشكال ذات المعاملات: إذ يمكن تطبيق هذه "الدوال" (أو بالأحرى، عائلات الدوال المفهرسة حسب النوع) على قيم من أنواع مختلفة، وعلى الرغم من اختلاف سلوكها باختلاف نوع الوسيط، إلا أن إضافة دعم لنوع جديد لا يتطلب جهدًا كبيرًا. وقد أظهر رالف هينز (2004) أنه يمكن تحقيق تأثير مماثل لفئات الأنواع المعرفة من قبل المستخدم باستخدام تقنيات برمجة معينة. كما اقترح باحثون آخرون مناهجًا لهذا النوع من العمومية وأنواع أخرى في سياق لغة هاسكل وامتداداتها (المناقشة أدناه).Show==show

بولي بي

كانت PolyP أول امتداد للغة البرمجة العامة Haskell . في PolyP، تُسمى الدوال العامة بالدوال متعددة الأنماط . تُقدم اللغة بنية خاصة تُتيح تعريف هذه الدوال متعددة الأنماط عبر الاستقراء البنيوي على بنية دالة النمط لنوع بيانات عادي. تُعد أنواع البيانات العادية في PolyP مجموعة فرعية من أنواع بيانات Haskell. يجب أن يكون نوع البيانات العادي t من النوع * → * ، وإذا كان a هو وسيط النوع الرسمي في التعريف، فيجب أن تكون جميع الاستدعاءات المتكررة لـ t على الشكل ta . تستبعد هذه القيود أنواع البيانات ذات الرتب الأعلى وأنواع البيانات المتداخلة، حيث تكون الاستدعاءات المتكررة على شكل مختلف. تُقدم دالة flatten في PolyP هنا كمثال:

تسطيح :: عادي d => d a -> [ a ] ​​تسطيح = cata flمتعدد الأنماط fl :: f a [ a ] ​​-> [ a ] ​​حالة f من g + h -> إما fl fl g * h -> \ ( x , y ) -> fl x ++ fl y () -> \ x -> [] Par -> \ x -> [ x ] Rec -> \ x -> x d @ g -> concat . flatten . pmap fl Con t -> \ x -> []كاتا :: منتظم d => ( FunctorOf d a b -> b ) -> d a -> b
لغة هاسكل العامة

تُعدّ لغة هاسكل العامة امتدادًا آخر للغة هاسكل ، وقد طُوّرت في جامعة أوتريخت في هولندا . وتشمل الامتدادات التي توفرها ما يلي:

  • تُعرَّف القيم المفهرسة حسب النوع بأنها قيمة مفهرسة على مختلف مُنشئات أنواع هاسكل (الوحدات، والأنواع الأولية، والمجاميع، والمنتجات، ومنشئات الأنواع المُعرَّفة من قِبل المستخدم). بالإضافة إلى ذلك، يُمكننا أيضًا تحديد سلوك القيم المفهرسة حسب النوع لمنشئ مُحدد باستخدام حالات المُنشئ ، وإعادة استخدام تعريف عام في تعريف آخر باستخدام الحالات الافتراضية .

يمكن تخصيص القيمة الناتجة المفهرسة حسب النوع لأي نوع.

  • الأنواع المفهرسة حسب النوع هي أنواع مفهرسة على أنواع، وتُعرَّف بإعطاء حالة لكل من * و k → k' . يتم الحصول على الحالات بتطبيق النوع المفهرس حسب النوع على نوع معين.
  • يمكن استخدام التعريفات العامة بتطبيقها على نوع أو صنف معين. يُسمى هذا التطبيق العام . وتكون النتيجة نوعًا أو قيمة، وذلك بحسب نوع التعريف العام المُطبق.
  • يُمكّن التجريد العام من تعريف التعريفات العامة عن طريق تجريد معلمة النوع (من نوع معين).
  • الأنواع المفهرسة هي أنواع يتم فهرستها على مُنشئات الأنواع. يمكن استخدامها لإعطاء أنواع للقيم العامة الأكثر تعقيدًا. ويمكن تخصيص الأنواع المفهرسة الناتجة لأي نوع.

على سبيل المثال، دالة المساواة في لغة هاسكل العامة: [ 34 ]

نوع Eq {[ * ]} t1 t2 = t1 -> t2 -> Bool نوع Eq {[ k -> l ]} t1 t2 = forall u1 u2 . Eq {[ k ]} u1 u2 -> Eq {[ l ]} ( t1 u1 ) ( t2 u2 )مكافئ { | ر :: ك | } :: المعادلة {[ ك ]} t t مكافئ { | الوحدة | } _ _ = مكافئ صحيح { | :+: | } eqA eqB ( Inl a1 ) ( Inl a2 ) = eqA a1 a2 مكافئ { | :+: | } eqA eqB ( Inr b1 ) ( Inr b2 ) = eqB b1 b2 مكافئ { | :+: | } eqA eqB _ _ = مكافئ خاطئ { | :*: | } eqA eqB ( a1 :*: b1 ) ( a2 :*: b2 ) = eqA a1 a2 && eqB b1 b2 eq { | كثافة العمليات | } = ( == ) مكافئ { | شار | } = ( == ) مكافئ { | بول | } = ( == )

ينظف

يقدم Clean البرمجة العامة القائمة على PolyP و Generic Haskell كما يدعمها GHC ≥ 6.0. وهو يقوم بمعاملة المعاملات حسب النوع مثل تلك، ولكنه يوفر التحميل الزائد.

لغات أخرى

تدعم لغات عائلة ML البرمجة العامة من خلال تعدد الأشكال البارامتري والوحدات العامة المسماة بالدوال. توفر كل من Standard ML و OCaml الدوال، وهي مشابهة لقوالب الفئات وحزم Ada العامة. كما ترتبط التجريدات النحوية في Scheme بالعمومية، فهي في الواقع مجموعة شاملة لقوالب C++.

قد تأخذ وحدة Verilog مُعاملًا واحدًا أو أكثر، وتُسند إليها قيمها الفعلية عند إنشاء الوحدة. ومن الأمثلة على ذلك مصفوفة سجلات عامة ، حيث يُحدد عرض المصفوفة عبر مُعامل. ويمكن لهذه المصفوفة، بالاشتراك مع متجه سلكي عام، أن تُنشئ وحدة تخزين مؤقت أو وحدة ذاكرة عامة بعرض بتات عشوائي من تنفيذ وحدة واحدة. [ 35 ]

تتمتع لغة VHDL ، المشتقة من لغة Ada، بقدرات عامة أيضًا. [ 36 ]

تتميز لغة C بخاصية تُسمى "التعبيرات العامة للأنواع" باستخدام الكلمة _Genericالمفتاحية: [ 37 ]. تُتيح هذه الخاصية للغة C إمكانية تحميل الدوال، ولكنها لا ترتبط بالبرمجة العامة. يُمكن تحقيق البرمجة العامة باستخدام المعالج المسبق لتعريف الكود العام، مع قيام توسيع الماكرو بدور إنشاء الكائنات . في بعض الأحيان، يُستخدم الامتداد غير القياسي "تعبيرات العبارات" لمحاكاة سلوك الدوال للكود العام. يُتيح الجمع بين هاتين الخاصيتين تنفيذ دالة max عامة، على غرار ما هو موجود في لغة C++.std::max

#define max(a,b) \  ({ typeof (a) _a = (a); \  typeof (b) _b = (b); \  _a > _b ? _a : _b; })

انظر أيضاً

مراجع

  1. لي، كينت د. (15 ديسمبر 2008). لغات البرمجة: منهج التعلم النشط . سبرينغر ساينس آند بيزنس ميديا. الصفحات 9-10 . ISBN  978-0-387-79422-8.
  2. ميلنر، ر .؛ موريس، ل.؛ نيوي، م. (1975). "منطق للدوال القابلة للحساب ذات الأنواع الانعكاسية والمتعددة الأشكال". وقائع مؤتمر إثبات البرامج وتحسينها .
  3. غاما، إريك؛ هيلم، ريتشارد؛ جونسون، رالف؛ فليسيدس، جون (1994). أنماط التصميم . أديسون-ويسلي. ISBN 0-201-63361-2.
  4. موسر وستيبانوف 1989 .
  5. ^ موسر ، ديفيد ر. ستيبانوف ، ألكسندر أ. البرمجة العامة (PDF) .
  6. ألكسندر ستيبانوف؛ بول ماكجونز (19 يونيو 2009). عناصر البرمجة . أديسون-ويسلي بروفيشنال. ISBN 978-0-321-63537-2.
  7. موسر، ديفيد ر.؛ ستيبانوف، ألكسندر أ. (1987). "مكتبة من الخوارزميات العامة في لغة آدا". وقائع المؤتمر الدولي السنوي لعام 1987 لجمعية ACM SIGAda حول لغة آدا - SIGAda '87 . الصفحات 216-225 . CiteSeerX 10.1.1.588.7431 . doi : 10.1145/317500.317529 . ISBN   0897912438. S2CID 795406 . 
  8. ألكسندر ستيبانوف ومينغ لي: مكتبة القوالب القياسية. تقرير فني من مختبرات إتش بي 95-11 (R.1)، 14 نوفمبر 1995
  9. ماثيو هـ. أوسترن: البرمجة العامة ومكتبة القوالب القياسية (STL): استخدام وتوسيع مكتبة القوالب القياسية للغة C++. شركة أديسون-ويسلي لونغمان للنشر، بوسطن، ماساتشوستس، الولايات المتحدة الأمريكية، 1998
  10. جيريمي ج. سيك، لي-كوان لي، أندرو لومسدين: مكتبة بوست للرسوم البيانية: دليل المستخدم ودليل المرجع. أديسون-ويسلي 2001
  11. ستيبانوف، ألكسندر. تاريخ موجز لـ STL (PDF) .
  12. 1 2 ستروستروب، بيارن. تطوير لغة في العالم الحقيقي ومن أجله: سي++ 1991-2006 (ملف PDF) . doi : 10.1145/1238844.1238848 . S2CID 7518369 . 
  13. لو روسو، جرازيانو. "مقابلة مع أ. ستيبانوف" .
  14. ^ البيت الخلفي ، رولاند. يانسون، باتريك؛ جيرنج، يوهان؛ ميرتنز، لامبرت (1999). البرمجة العامة – مقدمة (PDF) .
  15. لاميل، رالف؛ بيتون جونز، سيمون (يناير 2003). "تخلص من التعليمات البرمجية النمطية: نمط تصميم عملي للبرمجة العامة" (ملف PDF) . مايكروسوفت . تم الاطلاع عليه بتاريخ 16 أكتوبر 2016 .
  16. دوس ريس، غابرييل؛ يارفي، جاكو (2005). "ما هي البرمجة العامة؟ (نسخة أولية من مؤتمر LCSD'05)" (ملف PDF) . مؤرشف من النسخة الأصلية (ملف PDF) بتاريخ 25 ديسمبر 2005.
  17. ر. غارسيا؛ ج. جارفي؛ أ. لومسدين؛ ج. سيك؛ ج. ويلكوك (2005). "دراسة مقارنة موسعة لدعم اللغة للبرمجة العامة (نسخة أولية)". CiteSeerX 10.1.1.110.122 . 
  18. "نظرة عامة على قانون آدا لعام 2005 - جمعية موارد آدا" . www.adaic.org . تم الاطلاع عليه بتاريخ 6 أكتوبر 2025 .
  19. "نظرة عامة: الاستثناءات، والأرقام، والأنواع العامة، إلخ" . www.adaic.org . تم الاطلاع عليه بتاريخ 6 أكتوبر 2025 .
  20. "الوحدات العامة" . www.adaic.org . تم الاطلاع عليه بتاريخ 25 أبريل 2024 .
  21. 1 2 ""learn.adacore.com"" . learn.adacore.com . تم الاطلاع عليه بتاريخ 6 أكتوبر 2025 .
  22. ستروستروب، دوس ريس (2003): مفاهيم - خيارات التصميم للتحقق من وسيطات القوالب
  23. ستروستروب، بيارن (1994). "15.5 تجنب تكرار الكود". تصميم وتطور لغة C++ . ريدينغ، ماساتشوستس: أديسون-ويسلي. ص 346-348 . Bibcode : 1994dec..book.....S . ISBN  978-81-317-1608-3.
  24. برايت، والتر. "أنواع فولدمورت في د" . دكتور دوبس . تم الاسترجاع في 3 يونيو 2015 .
  25. بناء البرمجيات الموجهة للكائنات، برنتيس هول، 1988، وبناء البرمجيات الموجهة للكائنات، الطبعة الثانية، برنتيس هول، 1997.
  26. إيفل: اللغة، برنتيس هول، 1991.
  27. بلوخ 2018 ، ص 126، §الفقرة 28: تفضيل القوائم على المصفوفات.
  28. تاريخ الأنواع العامة في .NET/C#: بعض الصور من فبراير 1999
  29. ألباهاري 2022 .
  30. سي شارب: الأمس واليوم والغد: مقابلة مع أندرس هيلسبرغ
  31. البرمجة العامة في لغات C# و Java و C++
  32. تحليل الكود CA1006: تجنب تضمين الأنواع العامة في توقيعات الأعضاء
  33. قيود على معلمات النوع (دليل برمجة C#)
  34. دليل المستخدم العام للغة هاسكل
  35. فيريلوج بالأمثلة، القسم المتبقي للمراجعة . بلين سي. ريدلر، فول آرك برس، 2011. ISBN 978-0-9834973-0-1
  36. https://www.ics.uci.edu/~jmoorkan/vhdlref/generics.html مرجع VHDL
  37. مسودة لجنة WG14 N1516 — 4 أكتوبر 2010

مصادر

للمزيد من القراءة

  • غابرييل دوس ريس وجاكو يارفي، ما هي البرمجة العامة؟، LCSD 2005 مؤرشف في 28 أغسطس 2019 في Wayback Machine .
  • جيبونز، جيريمي (2007). باكهاوس، ر.؛ جيبونز، ج.؛ هينز، ر.؛ جيورينغ، ج. (محررون). البرمجة العامة لأنواع البيانات . المدرسة الربيعية للبرمجة العامة لأنواع البيانات 2006. سلسلة محاضرات في علوم الحاسوب. المجلد  4719. هايدلبرغ: سبرينغر. الصفحات 1-71 . CiteSeerX 10.1.1.159.1228 .  
  • ماير، برتراند (1986). "التعميم مقابل الوراثة". وقائع مؤتمر أنظمة ولغات وتطبيقات البرمجة كائنية التوجه - OOPSLA '86 . الصفحات 391-405 . doi : 10.1145/28697.28738 . ISBN  0897912047. S2CID 285030 . 
سي++، دي
سي شارب، دوت نت
دلفي، أوبجكت باسكال
إيفل
هاسكل
جافا