نقاش عن نقد المؤشرات pointers في إبداع
- طالب علم
- 2014-05-23T05:29:45+00:00
السلام عليكم
أخي وائل أشكر لك جهدك في تطوير إبداع و أدعو الله أن يوفقك
في كتابك "رسالة البرمجة بإبداع" أنتقدت المؤشرات وقلت
"تدخل المبرمج في تفاصيل يفترض أن تظل مقصورة على المبرمج الباني للمترجم لا مستخدم اللغة"(صفحة 224)
1- فكيف يأخي تريد بناء المترجم الخاص بإبداع بها بدون الإعتماد على فروع المؤشرات أمثال القوائم المترابطة LinkedList و غيرها
2- وكيف نبني نواة نظام تشغيل Kernel OS بدون مؤشرات
وغيرهم الكثير من البرامج الأساسية
و عليكم السلام و رحمة الله و بركاته
أشكر لك أخي الكريم اهتمامك بلغة إبداع و أساسيات تصميمها، و أدعو الله تعالي أن يوفقني و إياك لما فيه خير الدنيا و الآخرة.
في الواقع فإن نقطة عدم قبول وجود المؤشرات في لغة "إبداع" حساسة للغاية؛ لأنه يُفترض بإبداع أن تكون عامة الأغراض general purpose يمكن استخدامها في معظم الأعمال البرمجية (إن لم تكن كلها)، و من ضمن تلك الأغراض كتابة أنوية أنظمة التشغيل و أي برمجيات منخفضة المستوي low level و تخاطب العتاد hardware بشكل مباشر، و لكن في نفس الوقت فكونها لغة عالية المستوي high level يجب أن يجعلها بعيدة كل البعد عن حشر الخصائص منخفضة المستوي في الأكواد التي لا ينبغي أن توجد بها تلك الخصائص، يعني علي سبيل المثال لو أردتَ عمل برنامج عالي المستوي بلغة الـC فستضطر اضطراراً لاستخدام المؤشرات Pointers في الأكواد؛ لأن المؤشرات جزء لا يتجزأ من اللغة، رغم أن ذلك البرنامج قد لا يكون فيه أي نوع من أنواع التواصل مع العتاد !. و هكذا ستجد أنك تعاني من "المشاكل" التي يسببها وجود المؤشرات في برامجك التي لا تحتاجها، بينما ستجد في المعتاد أنك تحب المؤشرات و تجدها الطريقة المثالية حينما تتعامل مع العتاد.
إذاً ما الحل ؟
هل ستصمم لغتك المفضلة بحيث تخلو تماماً من المؤشرات لتنعم براحة البال مع معظم أنواع العمل البرمجي، و لكن تحرم مستخدميها من التعامل مع العتاد ؟، أم ستصممها بحيث تحتوي علي المؤشرات فتجعل مستخدميها يعانون من المشاكل التي تنتج عن كون لغتك عالية المستوي تدخلهم في تفاصيل لا تهمهم من قريب أو من بعيد ؟
الحل من وجهة نظري هو الاستغناء عن المؤشرات بوسيلة تحل محلها، بحيث يمكن بتلك الوسيلة التعامل مع العتاد بشكل مباشر. و لكن بشرط أن يكون في نفس الوقت من الصعب حشرها في البرامج التي لا تحتاجها. و قد وجدتُ أن أفضل وسيلة لفعل ذلك هي "الإجراءات المُخصَّصة"[1]. و هي الإجراءات التي لا تحتوي علي أكواد عادية، بل تحتوي علي أكواد مكتوبة بلغة التجميع assembly language، و يمكن عمل تحميل overload للإجراء المخصص عن طريق تغيير نوع المُعالِج processor الذي يعمل عليه ذلك الإجراء بمنتهي البساطة.
مثال:
إجراء مخصص إجراء1:
يخص بيئة_إنتل8086:
\ أكواد تجميع مناسبة /
إجراء مخصص إجراء1:
يخص بيئة_MIPS:
\ أكواد تجميع مناسبة /
بحيث حينما يتم استدعاء الإجراء "إجراء1" كما يلي:
إجراء1()
يقوم "أُبْدِع" (و هو المفسر القياسي standard interpreter للغة إبداع) باختيار التحميل المناسب لاستدعائه تبعاً لنوع المعالج الذي يعمل عليه البرنامج، أما لو كان البرنامج مترجَماً compiled فسيكون بإمكانك في عملية الترجمة تحديد نوع المُعالِج المُستهدَف؛ بحيث يتم اختيار الإجراءات المخصصة المكتوبة لأجله و يتم ضمها هي فقط إلي البرنامج التنفيذي النهائي.
و هكذا يكون بإمكاننا بناء برمجيات تعامل مع العتاد الصلب مباشرة، و في نفس الوقت لا نرغِم المستخدم علي التعامل مع الخصائص منخفضة المستوي في الأكواد التي لا يحتاج فيها للتعامل معها. صحيح أن الأمر يحتاج للكثير من الوقت للانتهاء من تصميم المواصفات القياسية للإجراءات المُخصصة بشكل جيد، و صحيح كذلك أن التعامل مع العتاد بهذا الشكل لن يكون بنفس سهولة التعامل معه بالمؤشرات، و لكن هذا (من وجهة نظري) ثمن بخس ندفعه للحصول علي التوازن المرغوب.
[1] يمكن القراءة أكثر عن الإجراءات المُخصَّصة في كتاب الرسالة، القسم الثاني: "عن المواصفات القياسية للغة البرمجة العربية إبداع"، باب "شرح المواصفات القياسية لإبداع"، فصل "الإجراءات"، نقطة "ما ينفرد به الإجراء المُخصَّص و لا تشاركه فيه الأنواع الأخري"، صفحة 144 من الإصدارة 1.2 من الرسالة.
لكن أخي التعامل مع الذاكرة عبر لغة التجميع يفقدنا بعض المميزات
أمثل التعامل مع التراكيب structure و الأصناف class بسهولة عبر المؤشرات
مثل مصفوفة متغيرة dynamic array من معلومات الطلاب
كيف نتعامل معه بسهولة عبر لغة التجميع؟
ولا ننسى مشكلة تعدد المعالجات
فوائد المؤشرات هي في الأصل قرارات تصميمية في لغة الـC، و يمكن الوصول إلي تلك الفوائد باستخدام طرق و تصميمات أخري، علي سبيل المثال إليك بعض تلك الفوائد و توضيح حصول إبداع عليها بشكل مختلف بدون استخدام المؤشرات:
- إعطاء حجم مرن للمصفوفات (أي dynamic arrays)، و يمكن فعل هذا بمنتهي البساطة في إبداع عن طريق إسناد القيم الجديدة إلي الجدول (و هو البديل للمصفوفات في إبداع) كما في المثال التالي:
رقم{1} الأرقام = {1، 2، 3}
أو عن طريق الإجراء القياسي "جديد"، كما في المثال:
\ حجز أماكن فارغة للجدول الأرقام /
رقم{1} الأرقام = جديد(3)
و هكذا فلا نحتاج للمؤشرات في هذه الحالة.
- التمرير بالمرجع pass by reference؛ و فائدته العظمي هي محاكاة إعادة أكثر من قيمة من داخل الدوال functions، حيث -كما هو معلوم- أن الـC لا تدعم إلا إعادة قيمة واحدة من داخل الدالة؛ محاكاة للنموذج الرياضي، و في إبداع (كما في لغة go) يمكن إعادة أكثر من قيمة من داخل الإجراء (أكتب حالياً مقالاً أوضح فيه الأسباب التي تجعل إعادة أكثر من قيمة من داخل الإجراءات في إبداع أمراً منطقياً للغاية).
مثال (أرجو الانتباه إلي أن النقاط الأربعة في بدايات بعض الأسطر هي مجرد بديل للإزاحة indentation لأنها لن تظهر بشكل صحيح في التعليق):
رقم رقم1 رقم2
رقم1، رقم2 = إجراء1()
أكتب.رقم.سطر(رقم1)
أكتب.رقم.سطر(رقم2)
إجراء (رقم رقم1 رقم رقم2) إجراء1:
....رقم1 = 1
....رقم2 = 2
تمرير كائنات الهياكل structures objects بالمرجع لأنها في العادة تُمرَّر بالقيمة، و هذا لا حاجة له في إبداع لأنها لا تحتوي علي الـstructures من الأصل (مثل لغة java).
لعمل مصفوفة مصفوفات jagged array، و في إبداع يمكن عمل هذا ببساطة كما في المثال التالي:
رقم{1} أرقام1 = {1، 2، 3} أرقام2 = {4، 5، 6، 7}
رقم{2} أرقام3 = { أرقام1 ، أرقام2 }
رقم الرقم
بينما الرقم في أرقام3:
....أكتب.رقم.سطر(الرقم)
رقم{2} أرقام4 = { {8، 9} ، {10، 11، 12} }
بينما الرقم في أرقام4:
....أكتب.رقم.سطر(الرقم)
و يمكنك أن تقيس بقية فوائد المؤشرات علي ما فات، و أرجو الانتباه إلي أنني لا أقول أن هذه عيوب في لغة الـC (إذا ما نظرتَ إليها علي أنها لغة تجميع عالية المستوي high level assembly language كما ينظر إليها linus Torvalds و كما أنظمر إليها أنا أيضاً)؛ بل أقول أنها قرارات تصميمة جعلت الـC تعتمد علي المؤشرات بشكل تام، لكن من الممكن الحصول علي فوائدها عن طريق تصميمات مختلفة، و بالتالي فليست المؤشرات هي عصا الساحر التي يجب أن تكون موجودة في اللغة حتي تقوم بتلك الأمور و إلا صارت اللغة هشة و ضعيفة.
و أكرر أن التعامل مع العتاد باستخدام المؤشرات في الغالب أسهل من التعامل معه عن طريق الإجراءات المخصصة، و لكن ميزة الإجراءات المخصصة أنك لستَ مجبراً علي حشرها في كل مكان كما في حالة المؤشرات في لغة الـC و التي لا يمكنك ألا تستخدمها؛ لأنها كما قلنا جزء لا يتجزأ من اللغة، و يتم استخدامها كما أوضحنا في النقاط السابقة لإعطاء اللغة قدرات ستفقدها لو حذفنا المؤشرات منها.
و انظر مثلاً إلي لغة go و هي لغة حديثة تم تصميمها و بناؤها في google، علي يد مجموعة من أنبغ علماء الحوسبة (و منهم Ken Thompson الشهير، و الذي يُعتبر من أبرز الأسماء المرتبطة بتاريخ لغة الـC)، ففيها تم الاستغناء عن حساب المؤشرات pointers arithmetic رغم أن القوة الحقيقية للمؤشرات تكمن في إمكانية إخضاعها للعمليات الحسابية !، و تعليلهم لهذا الأمر يمكن قراءته علي الرابط:
و مختصر كلامهم أنه يمكن الحصول علي مميزات حساب المؤشرات عن طريق التحسينات التي يجريها المُترجِم compiler، و في نفس الوقت نتلافي الأغلاط المنطقية التي تحدث عند التعامل مع الذاكرة بشكل مباشر زيادةً عن اللزوم. كما أن وجود حساب المؤشرات يجعل بناء "مُجمِّع النفايات garbage collector" أصعب (صدق أو لا تصدق: لغة go لها مجمع نفايات).
و من المخطط أن يكون لإبداع مجمع نفايات يجعل التعامل مع الذاكرة أكثر كفاءة و يزيح عبئه عن كاهل المبرمج، و هكذا لا تحتاج للتعامل مع الذاكرة من حيث الحجز و التفريغ أو ما شابه. و إن احتجنا إلي مثل تلك الأمور فمن الممكن دعمها عن ريق إجراءات قياسية في اللغة تتولي هذه المسئولية ببساطة (و إن كنت أستبعد هذا الأمر بشكل شبه تام).
وهل لك أخي أن تعطيني مثالا على إستعمال لغة التجميع في إدارة الذاكرة من حجز وتمديد وتقليص وتحرير
ولو كان مثلا وهميا
مجتمع يهتم بكل ما يخص مشروع "البرمجة بإبداع"، سواءٌ أكان أخباراً أو نقاشات أو غيرهن.