الدالات الضمنية والدالات المغلَّفة

متوفرة في الإصدار 0.8 من لغة الأسس

ملاحظة: لغرض الحفاظ على تلوين الشفرة المصدرية ضمننا الأمثلة كصور بدل النصوص. للحصول على النص يمكن تنزيل الملف من هنا.

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

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

ولكي نستخدم هذه الدالة لنفترض أن لدينا الصنف التالي، ومنه مجموعة بيانات:

لكي نستخدم دالة الترتيب على البيانات أعلاه سنحتاج لتعريف دالات المقارنة كما في المثال التالي:

image

image

لاحظ الحاجة لتعريف دالة منفصلة كلما أردنا ترتيب القيود بشكل معين. هنا يمكننا استخدام الدالات الضمنية لتعريف الدالة ضمن عملية الترتيب بدل تعريفها كدالة منفصلة، كما في المثال التالي:

image

image

لنأخذ مثالاً آخر أكثر تعقيدًا، وهو صنف يستخدم لتصفية عناصر مصفوفة. لكنه يعمل كمكرر (iterator) بدل تصفية العناصر دفعة واحدة.

لاستخدام هذا المكرر تحتاج لتهيئته بإعطاءه مصفوفة ودالة مقارنة، كما في المثال التالي الذي يصفي ثم يطبع القيود التي لأعمار تزيد على 36:

image

image

لكن مذا لو أردنا تعميم دالة هات_مصفي_لعمر_أكبر_من_36 لتستلم العمر المطلوب كمتغير؟

image

image

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

الدالة المغلفة (closure) تشبه الدالة الضمنية لكنها تختلف عنها بأنها تُغلف بالبيئة الخارجية التي تستخدمها، أي أنها تحمل معها نسخة من البيانات الخارجية التي يحتاجها متن الدالة. لاستخدام الدالة المغلفة نحتاج أولاً لشمولها في المشروع ومن ثم تغيير صنف مـصفي يستخدمها بدل مؤشر الدالات التقليدي، كما في المثال التالي:

ثم نحتاج لاستخدام الدالة المغلفة بدل الدالة الضمنية في دالة هات_مصفي_لعمر_أكبر_من، كما يلي:

image

image

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

1 Like