حل أسماء اللغات البرمجية
في لغات البرمجة ، يُقصد بحل الأسماء حل الرموز المميزة داخل تعبيرات البرنامج إلى مكونات البرنامج المقصودة.
ملخص
تشير التعابير في برامج الحاسوب إلى المتغيرات وأنواع البيانات والدوال والفئات والكائنات والمكتبات والحزم وغيرها من الكيانات بالاسم. في هذا السياق، يشير حلّ الأسماء إلى ربط هذه الأسماء، التي قد لا تكون فريدة بالضرورة، بكيانات البرنامج المقصودة. وتُعدّ الخوارزميات التي تحدد ما تشير إليه هذه المعرّفات في سياقات محددة جزءًا من تعريف اللغة.
يتأثر تعقيد هذه الخوارزميات بمدى تطور اللغة. على سبيل المثال، عادةً ما يتضمن تحليل الأسماء في لغة التجميع بحثًا بسيطًا واحدًا في جدول ، بينما يكون تحليل الأسماء في لغة C++ معقدًا للغاية لأنه يتضمن ما يلي:
- مساحات الأسماء ، التي تجعل من الممكن أن يكون للمعرف معانٍ مختلفة اعتمادًا على مساحة الاسم المرتبطة به؛
- تُتيح النطاقات إمكانية أن يحمل المعرّف معاني مختلفة على مستويات نطاق مختلفة، ويتضمن ذلك قواعد متنوعة لتجاوز النطاق وإخفائه. في أبسط مستوياته، يسعى تحليل الأسماء عادةً إلى إيجاد الربط في أصغر نطاق مُحيط، بحيث تتجاوز المتغيرات المحلية، على سبيل المثال، المتغيرات العامة؛ وهذا ما يُسمى بالتظليل .
- قواعد الرؤية ، التي تحدد ما إذا كانت المعرفات من مساحات أسماء أو نطاقات محددة مرئية من السياق الحالي؛
- التحميل الزائد ، مما يجعل من الممكن أن يكون للمعرف معانٍ مختلفة اعتمادًا على كيفية استخدامه، حتى في مساحة اسم أو نطاق واحد؛
- إمكانية الوصول ، والتي تحدد ما إذا كانت المعرفات من نطاق مرئي يمكن الوصول إليها بالفعل وتشارك في عملية حل الأسماء.
ثابت مقابل ديناميكي
في لغات البرمجة ، يمكن إجراء تحليل الأسماء إما في وقت الترجمة أو في وقت التشغيل . يُطلق على الأول اسم تحليل الأسماء الثابت ، بينما يُطلق على الثاني اسم تحليل الأسماء الديناميكي .
من المفاهيم الخاطئة الشائعة أن الكتابة الديناميكية تعني بالضرورة حل الأسماء ديناميكيًا. على سبيل المثال، لغة إرلانج ذات كتابة ديناميكية، لكنها تعتمد على حل الأسماء الثابت. مع ذلك، فإن الكتابة الثابتة تعني بالضرورة حل الأسماء الثابت.
يُمكّن حلّ الأسماء الثابت من اكتشاف استخدام المتغيرات غير المُدرجة في النطاق أثناء الترجمة، مما يمنع أخطاء المبرمجين. أما اللغات التي تعتمد على حلّ النطاق الديناميكي، فتُضحّي بهذا الأمان مقابل مزيد من المرونة؛ إذ يُمكنها عادةً تعيين واسترجاع المتغيرات في النطاق نفسه أثناء التشغيل.
على سبيل المثال، في بيئة بايثون التفاعلية REPL :
>>> number = 99 >>> first_noun = "problems" >>> second_noun = "hound" >>> # يتم تحديد المتغيرات المستخدمة في وقت التشغيل >>> print ( f "لدي { number } { first_noun } لكن { second_noun } ليس واحدًا منها." ) لدي 99 مشكلة لكن الكلب ليس واحدًا منها.مع ذلك، فإن الاعتماد على حلّ الأسماء الديناميكي في الشيفرة البرمجية غير مُستحبّ من قِبل مجتمع بايثون. [ 1 ] [ 2 ] وقد تُزال هذه الميزة أيضًا في إصدار لاحق من بايثون. [ 3 ]
من أمثلة اللغات التي تستخدم حل الأسماء الثابت: C و C++ و E و Erlang و Haskell و Java و Pascal و Scheme و Smalltalk . أما من أمثلة اللغات التي تستخدم حل الأسماء الديناميكي: بعض لهجات Lisp و Perl و PHP و Python و Rebol و Tcl .
إخفاء الاسم
يحدث الإخفاء عندما يُستخدم نفس المعرّف لكيانات مختلفة في نطاقات معجمية متداخلة. على مستوى المتغيرات (وليس الأسماء)، يُعرف هذا باسم تظليل المتغيرات . يُخفي المعرّف I' (للمتغير X') المعرّف I (للمتغير X) عند تحقق شرطين.
- أنا يحمل نفس اسمي
- يتم تعريف I' في نطاق يمثل مجموعة فرعية من نطاق I
يقال إن المتغير الخارجي X مظلل بواسطة المتغير الداخلي X'.
على سبيل المثال، يقوم المعامل "foo" بحجب المتغير المحلي "foo" في هذا النمط الشائع:
private int foo ; // تم تعريف الاسم "foo" في النطاق الخارجيpublic void setFoo ( int foo ) { // تم تعريف الاسم "foo" في النطاق الداخلي، وهو خاص بالدالة. this . foo = foo ; // بما أن "foo" سيتم العثور عليه (وحله) أولاً في النطاق "الأعمق"، // من أجل الكتابة بنجاح فوق القيمة المخزنة للخاصية "foo" // بالقيمة الجديدة للمعامل الوارد "foo"، يتم التمييز // بين "this.foo" (خاصية الكائن) و"foo" (معامل الدالة). }public int getFoo () { return foo ; }يمكن أن يتسبب إخفاء الأسماء في حدوث تعقيدات في تحميل الوظائف الزائدة ، وذلك بسبب عدم حدوث التحميل الزائد عبر النطاقات في بعض اللغات، وخاصة C++، مما يتطلب إعادة تعريف جميع الوظائف المحملة بشكل زائد أو استيرادها بشكل صريح إلى مساحة اسم معينة.
إعادة تسمية الأسماء أبجديًا لتسهيل عملية تحديد الاسم
في لغات البرمجة ذات النطاق المعجمي التي لا تعكس أسماء المتغيرات، يمكن استخدام التحويل ألفا (أو إعادة التسمية ألفا) لتسهيل عملية تحديد الأسماء من خلال إيجاد بديل يضمن عدم حجب أي اسم متغير لاسم آخر في نطاقه. كما تُسهّل إعادة التسمية ألفا تحليل الكود الثابت ، إذ يكفي أن يفهم مُعيد التسمية ألفا قواعد نطاق اللغة.
على سبيل المثال، في هذا الكود:
class Point { private : double x , y ;public : Point ( double x , double y ) { // تم تعريف x و y هنا لإخفاء المتغيرات الخاصة setX ( x ); setY ( y ); }void setX ( double newx ) { x = newx ; } void setY ( double newy ) { y = newy ; } }في دالة إنشاء الكائن Point ، يتم إخفاء متغيرات النسخة x و y بواسطة متغيرات محلية تحمل نفس الاسم. يمكن إعادة تسمية هذا المتغير إلى:
class Point { private : double x , y ;public : Point ( double a , double b ) { setX ( a ); setY ( b ); }void setX ( double newx ) { x = newx ; } void setY ( double newy ) { y = newy ; } }في الإصدار الجديد، لا يوجد إخفاء، لذلك من الواضح على الفور أي الاستخدامات تتوافق مع أي التصريحات.
انظر أيضاً
مراجع
- ↑ "[Python-Ideas] دالة str.format المساعدة" . 9 مايو 2009. تم الاطلاع عليه بتاريخ 23 يناير 2011 .
- ↑ "8.6. تنسيق السلاسل النصية باستخدام القاموس" . diveintopython.org . مارك بيلغريم . تم الاطلاع عليه بتاريخ 23 يناير 2011 .
- ↑ "9. الفئات - توثيق بايثون" . تم الاطلاع عليه بتاريخ 24 يوليو 2019.
من المهم إدراك أن النطاقات تُحدد نصيًا: النطاق العام لدالة مُعرَّفة في وحدة نمطية هو مساحة اسم تلك الوحدة، بغض النظر عن مكان استدعاء الدالة أو الاسم المستعار الذي تُستدعى به. من ناحية أخرى، يتم البحث الفعلي عن الأسماء ديناميكيًا، أثناء التشغيل - ومع ذلك، يتطور تعريف اللغة نحو حل الأسماء الثابت، أثناء "الترجمة"، لذا لا تعتمد على حل الأسماء الديناميكي! (في الواقع، يتم تحديد المتغيرات المحلية بشكل ثابت بالفعل).
- مكتبات الحاسوب
- بناء المترجم
