اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'لغة برمجة'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

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

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 3 نتائج

  1. سنبدأ في هذا المقال من سلسلة برمجة الذكاء الاصطناعي في تعلم أساسيات لغة بايثون، وهي من أهم لغات البرمجة على الإطلاق المستخدمة في مجال الذكاء الاصطناعي، ولكنها ليست لذلك فقط؛ إذ تُستعمَل لغة بايثون في كثير من المجالات الأخرى مثل برمجة المواقع وبرامج سطح المكتب وأنظمة التشغيل وغيرها. قبل البدء في أساسيات لغة بايثون: ما هي لغة البرمجة بايثون؟ تعني بايثون في اللغة الإنجليزية نوعًا من الثعابين الكبيرة، لكنها لم تُسمى لذلك بل سُمِّيَت بهذا الاسم تيمنًا ببرنامج ترفيهي قدَّمته قناة BBC يحمل اسم Monty Python’s Flying Circus، وذلك بعد ما شاهده مخترع اللغة. وتُعَدّ بايثون لغةً برمجيةً عامةً، أي تستطيع استخدامها في مجالات عدة على عكس بعض اللغات الأخرى التي تتخصص في مجال ما دونًا عن الآخر، كما تُعَدّ لغةً بسيطةً ومتطورةً للغاية، بالإضافة إلى أنها تدعم البرمجة كائنية التوجه Object Oriented Programming أو OOP اختصارًا. صُنِعت بايثون بواسطة الهولندي جايدو فان روسم Guido van Rossum، وأُصدِرت في شهر 11 من عام 1994م بعدما عُمِل عليها في معهد الأبحاث القومي للرياضيات وعلوم الحاسوب في هولندا، في حين نُشر الإصدار الثاني من بايثون في عام 2000م؛ أما الإصدار الثالث، فقد نُشِر في عام 2008م وهو الإصدار المستخدم حاليًا من بايثون لأن الإصدار الثاني السابق قد توقف دعمه وتطويره. خصائص لغة بايثون تُعَدّ لغة بايثون لغةً مفتوحة المصدر تجد شيفرتها المصدرية على موقع GitHub، وبالتالي يستطيع أيّ مبرمج المشاركة في تطوير اللغة. وبحسب موقع جيت هاب GitHub، فقد شارك أكثر من 1200 شخص حول العالم في تطويرها، كما تُعرَف بايثون بسهولة تراكيب الجمل فيها Syntax التي تشبه تركيب اللغة الإنجليزية بنسبة كبيرة، وهي لغة مهمة جدًا للطلبة والباحثين والمهنيين على حد سواء في الكثير من النطاقات العلمية والعملية. وتتميز بايثون بالميزات التالية: لغة برمجة مفسرة Interpreted تدعم البرمجة الكائنية مناسبة للمبتدئين مدعومة بالكثير من المكتبات لغة مجانية ومفتوحة المصدر تستخدم في العديد من التخصصات لغة برمجة مفسرة Interpreted أي أنها تُنفَّذ مباشرةً مثل لغة PHP ولغة Perl، ولا تحتاج إلى تصريف كامل الشيفرة أولًا ثم تنفيذها مثل لغة C، فلا يتطلب الأمر تحويل الشيفرة الخاصة بك إلى شيفرة ثنائية تفهمها الآلة أولًا لتبدأ عملية التنفيذ، وهذا قد يميز شيفرات بايثون في سرعة تنفيذها أثناء البرمجة. وفي تلك النقطة بالتحديد قد يختلف بعض المبرمجين، فالبرغم من تصنيف بايثون أنها لغة مفسَّرة، إلا أنّ الشيفرة تُصرَّف compiled أولًا ليستطيع المُفسِّر فهمها قبل تنفيذها، لذلك قد تجد بعض النقاشات المتباينة حول بايثون لمحاولة تصنيفها تصنيفًا دقيقًا. يذهب بعض المبرمجين إلى القول بأنّ لغة بايثون لها طريقتها الخاصة في تلك النقطة، فالأمر معقَّد ولا نريد الخوض فيه. تدعم البرمجة الكائنية تدعم بايثون نمطًا يُدعى الكائنية في البرمجة Object-oriented programming (أو تدعى الشيئية أحيانًا)، وهو نمط شهير ومهم، إذ تُكتَب الشيفرة ويُتعامَل مع أجزاء التطبيق على أساس الكائنات Objects، وهو نمط غير مدعوم في بعض اللغات القديمة مثل لغة C، كما أنها تدعم البرمجة الوظيفية Functional والهيكلية Structured وغيرها. أساسيات لغة بايثون مناسبة للمبتدئين تُعَد بايثون مناسبةً جدًا للمبتدئين، حيث أنَّ صياغة الجمل فيها بسيطة للغاية، ولا يتطلب الأمر الدخول في تفاصيل كثيرة عند كتابتها؛ لذلك فهي سهلة التعلم والقراءة. مدعومة بالكثير من المكتبات يمكنك في بايثون إيجاد مكتبات بسيطة الاستخدام تستطيع بها برمجة تطبيقات معقدة جدًا، فهي لغة لديها أرشيف واسع من المكتبات في كافة المجالات تقريبًا. لغة مجانية ومفتوحة المصدر توجد بعض لغات البرمجة غير المجانية، أي أنك مُطالَب بدفع الأموال لشركة ما، كي تستطيع استخدام تلك اللغة، كما أنَه لا يمكنك ولا يمكن لأيّ شخص آخر إصلاح مشكلة ما في اللغة أو أن يطوِّر ميزةً أو خاصيةً جديدةً في اللغة، فالشيفرة المصدرية في تلك الحالة تقع تحت أيدي الشركة المصنعة فقط، وهي الوحيدة التي يحق لها تطوير اللغة أو إصلاح مشاكلها أو إصدار نسخ جديدة منها. أما في بايثون والعديد من اللغات المجانية المفتوحة المصدر، فالأمر مختلف إذ عمِل على على لغة بايثون أكثر من 1200 شخص حول العالم، فالشيفرة المصدرية للغة موجودة ومتاحة على موقع GitHub، ويمكن لأيّ شخص له الخبرة والمعرفة الكافية أن يطوِّر أو يعدِّل ميزةً ما، كما يستطيع مجتمع مبرمِجي بايثون على الإنترنت من المشاركة بآرائهم في تطوير اللغة، فالأمر بالجميع وللجميع، وهي لغة مجانية بالكامل تستطيع استخدامها في أيّ مشروع خيري أو تجاري، بدون أية مشاكل قانونية على الإطلاق. تستخدم في العديد من التخصصات لا يقتصر الأمر أبدًا على برمجة الذكاء الاصطناعي وتعلّم الآلة، ولا يقتصر على برمجة مواقع الويب أيضًا، إذ تُعَدّ بايثون من أكثر لغات البرمجة انتشارًا وتوغلًا في العديد من المجالات في حياتنا اليومية، وفي التخصصات العلمية والأبحاث ومعامل ومختبرات الجامعات حول العالم، لذلك بتعلُُّمك للغة بايثون فإنّ الأمر لا يقتصر على فرصة عمل في مجال الذكاء الاصطناعي فحسب، وإنما تستطيع استخدام معرفتك وخبرتك في بايثون في مجالات أخرى تفتح عليك أبواب دخل إضافية. تُشتهر بايثون أيضًا في استخدامها في برمجة المواقع وتطبيقات سطح المكتب وبرمجة برامج تجارية عبر بايثون مثل أودوو Odoo الذي يُعَدّ أشهرها وله متخصصين وشركات تجارية تعتمد كليةً على استخدامه. كما أنَّ لغة بايثون كما أوردنا تدخل في الكثير من مجالات البحث العلمي، فهي من أكثر اللغات التي تحتوي على مكتبات تهدف إلى خدمة مجالات البحث العلمي والرياضيات من الذكاء الاصطناعي حتى التغير المناخي وتُعَد دراسة لغة بايثون أمرًا أساسيًا بالنسبة لمبرمجي الذكاء الاصطناعي، لذلك سنبدأ الآن في دراسة أساسيات اللغة حتى نكون على قدر من المعرفة المطلوبة لنبدأ في دراسة وتطبيق الخوارزميات الأساسية في تعلُّم الآلة. لذلك فإن السهل البدء بتعلم أساسيات بايثون والبدء في عالم البرمجة بسهولة. تثبيت لغة بايثون أول ما نبدأ به في تعلم أساسيات لغة Python ولكي تستطيع العمل بلغة بايثون، يجب عليك أولًا تثبيت البرنامج الذي يفهم اللغة ثم ينفذها، وذلك لكي يستطيع حاسوبك التعرف على الأوامر التي تكتبها لتعمل عليه بصورة صحيحة، لذلك من الضروري أن تكون أول خطوة نقوم بها هي تثبيت لغة بايثون على حاسوبك. يختلف أمر تثبيت اللغة باختلاف نظام التشغيل، فإذا كان حاسوبك مثلًا يعمل على نظام لينكس Linux، فعلى الأرجح أنّ حاسوبك مثبَّت عليه بالفعل لغة بايثون، وللتأكد من ذلك يمكنك فتح الطرفية Terminal ثم كتابة الأمر الآتي: >> python --version أول الأمر التالي بالنسبة للإصدار الثالث: >> python3 --version إذا كانت بايثون مثبتةً بالفعل على حاسوبك، فسيظهر لك رقم الإصدار المثبَّت، والجدير بالذكر أنه يجب أن يكون الإصدار المثبت لديك هو الإصدار الثالث، وبالتالي يجب بدء رقم الإصدار بالرقم 3؛ أما إذا لم تكن اللغة مثبتةً على حاسوبك، فيمكنك ذلك عبر تنفيذ الأمر الآتي في الطرفية Terminal على لينكس. >> sudo apt-get install python3.6 أما إذا كنت مستخدِمًا لنظام التشغيل ماك macOS بمختلف إصداراته، فعلى الأغلب أيضًا أنّ لغة بايثون مثبتة بالفعل على حاسوبك، وتستطيع اختبار ذلك عبر الأمر السابق ذكره بخصوص نظام لينكس، فإذا لم تكن اللغة مثبتةً، فيمكنك ببساطة تثبيتها مثل أيّ برنامج أخر عن طريق الموقع الرسمي للغة /Python. بعد تثبيت اللغة بالطرق الموضحة أعلاه في نظامي لينكس وماكينتوش، فمن المحتمل ألا يعمل أمر التحقق من الإصدار وألا يكون جاهزًا للعمل بعد، إذ أنه قد لا يُتعرَّف على برنامج بايثون عندما تُنفِّذ الأمر التالي: >> python --version ولحل ذلك يجب تنفيذ الأمر التالي في الطرفية Terminal: >> export PYTHONPATH=/usr/local/bin/python3.6 مع تغيير كلمة python3.6 لأنها قد تختلف حسب الإصدار الذي ثبَّته؛ لذلك يجب التحقق من ذلك المسار على حاسوبك أولًا لترى أيّ الإصدارات يجب استدعاؤها في الأمر السابق. أما في حالة مستخدمي نظام الويندوز، فالأمر بسيط للغاية، إذ تستطيع تنزيل برنامج اللغة من الموقع الرسمي السالف ذكره، ثم تثبيته مثل أيّ برنامج آخر على حاسوبك دون تعقيدات قد لا يعمل كذلك أمر التحقق من إصدار اللغة بصورة تلقائية بعد التثبيت، ولحل ذلك ببساطة يمكنك فتح موجِّه الأوامر Command Prompt في ويندوز ثم تنفيذ الأمر الآتي: >> %path%;C:\Python مع الأخذ في الحسبان إمكانية تغيير النص C:\Python إذا كنت قد تثبَّت اللغة في مسار آخر على حاسوبك أثناء عملية التثبيت. استعمال بايثون مع خدمة Google Colab ضمن أساسيات لغة بايثون وبالرغم من سهولة عملية تثبيت بايثون على حاسوبك، فإنه ليس من الضروري فعلًا فعل تلك الخطوات السالف ذكرها، فقد أصدرت شركة جوجل مؤخرًا ما يُدعى Google Colaboratory عبر موقع الأبحاث الخاص بها \colab.research، وبالتالي تستطيع ببساطة استخدام تلك الخاصية بإنشاء ذلك النوع من الملفات على خدمة Google Drive الموجودة مجانيًا لأي عنوان بريد إلكتروني مُسجَّل على Gmail، بعدها يمكنك البدء في كتابة وتنفيذ شيفرة البايثون الخاصة بك عبر الإنترنت دون الحاجة إلى الدخول في الكثير من التعقيدات والمشاكل التقنية أثناء تعلمك، أو حتى أثناء عملك في برمجة الذكاء الاصطناعي. كما تحتوى تلك الخدمة تلقائيًا على معظم وأهم مكتبات بايثون المستخدَمة في مجال الذكاء الاصطناعي عامةً ومجال تعلّم الآلة خاصةً، فنجد مثلًا تلقائيًا في تلك الخدمة أنّ مكتبات متخصصة في الرياضيات مثل Numpy، ومكتبات متخصصة في رسم البيانات مثل Matplotlib ومكتبات متخصصة في خوارزميات تعلّم الآلة مثل Keras، ومكتبات متخصصة في التعلّم العميق والشبكات العصبية مثل Tensorflow …إلخ مثبتة ومتاحة للاستخدام مباشرةً. أُصدِرت الخدمة أساسًا للتسهيل على العاملين في مجال برمجة الذكاء الاصطناعي، وبالأخص تعلُّم الآلة ليستطيع المبرمج مشاركة الشيفرة المصدرية الخاصة به مع نتائج هذه الشيفرة والملاحظات مع شركائه في العمل أو أي شخص آخر، وهي خدمة سحابية بالكامل، أي أنها تعمل عبر الإنترنت ولا تحتاج إلى أي متطلبات أو إمكانيات في حاسوبك، فكل ما تحتاجه لاستخدام الخدمة هي وصلة الإنترنت وعنوان بريد إلكتروني من Gmail. أفضِّل شخصيًا استخدام تلك الخدمة أثناء التعلم لأنها بسيطة وسهلة، وتحتوي تلقائيًا على الكثير من مكتبات بايثون الخاصة بالذكاء الاصطناعي التي قد يكون تثبيت بعضها عملًا شاقًا إذا حدث خطأ ما أثناء التثبيت والسبب الآخر الذي يدفعني إلى التوصية باستخدام تلك الخدمة بشدة، هو عدم امتلاك بعض أجهزة الحاسوب للإمكانيات اللازمة لتشغيل نماذج تعلّم الآلة، إذ تحتاج بعض الخوارزميات إلى ذاكرة عشوائية RAM كبيرة ليُدرَّب النموذج تدريبًا صحيحًا، وذلك اعتمادًا على حجم البيانات المتدفقة إلى النموذج. ولمزيد من التفاصيل، ارجع إلى مقال دليل استخدام Google Colab. برنامجك الأول في لغة بايثون من أساسيات لغة بايثون أنك تستطيع البدء في كتابة برنامجك الأول بعدة من الطرق، أولها عبر كتابة الشيفرة مباشرةً في الطرفية Terminal في لينكس وماك وموجِّه الأوامر Command Prompt في ويندوز؛ وثانيها، كتابة البرنامج في ملف أو عدة ملفات منفصلة، ثم تشغيلها عبر البرامج السابق ذكرها؛ أما ثالثها فتكون عن طريق خدمة Google Colab التي ذكرناها سابقًا. أما كتابة الشيفرة مباشرة في الطرفية وموجه الأوامر، فالخطوات لذلك بسيطة، وكل ما عليك فعله بعد فتح أحد تلك البرامج حسب نظام التشغيل الخاص بحاسوبك هو تنفيذ الأمر الآتي: >> python أو الأمر التالي بالنسبة للإصدار 3 من بايثون: >> python3 تستطيع بعد ذلك كتابة أوامر بايثون مباشرةً وتنفيذها، كما يمكنك الخروج من هذه الشاشة بعد ذلك عبر كتابة الأمر الآتي: >> quit() وأما كتابة الشيفرة في ملف منفصل، فتستطيع إنشاء ملف جديد في أيّ مكان في حاسوبك وتسميته بأيّ اسم تريده، كما يفضَّل أن يكون الاسم معبِّرًا، فالتسمية البسيطة والمعبِّرة من الأمور المهمة. احفظ بعد ذلك الملف بصيغة py -وهي صيغة اختصارية لكلمة Python-، فإذا كان مثلًا اسم الملف Program، فستكون التسمية الكاملة للملف Program.py، ثم اذهب إلى المكان المخزن فيه الملف غبر الطرفية أو موجِّه الأوامر، وبعدها نفِّذ الأمر التالي: >> python Program.py والطريقة الثالثة، تستطيع ببساطة استخدام Google Colab من خدمة Google Drive ومن ثم إنشاء ملف جديد من نوع Google Colaboratory، ثم البدء في إضافة الفقرات، فيمكنك إضافة شيفرات بايثون في ذلك الملف بجانب نصوص عادية وبذلك تستطيع كتابة ملاحظات وتعليقات بين أسطر الشيفرات ويمكنك في الوقت نفسه تنفيذ الشيفرات ككل أو أجزاء محددة. سنبدأ بأمر الطباعة لكتابة أول برنامج بايثون خاص بك، فوظيفة أمر الطباعة في بايثون وفي كثير من اللغات الأخرى، هي طباعة نص على شاشة المستخدِم عند تشغيل البرنامج، وقد ذكرنا ذلك الأمر سابقًا عند الحديث عن الخوارزميات في البرمجة، فعند كتابة الأمر print والذي يعني اطبع بالإنجليزية، فيجب إلحاقه بقوسين، ومن ثم فتح علامات تنصيص بداخل القوسين تحتوى على الجملة التي نريد طباعتها. print ("Hello World") قد يُستخدَم الأمر print لطباعة النصوص على الشاشة أو لطباعة الأرقام، فالأمر سيان في حالة الطباعة، ولكن لا يجب إضافة علامات تنصيص في حالة طباعة الأعداد. print (3) >> 3 كما أنه من الممكن طباعة نتيجة عملية رياضية بسيطة مثل عمليات الجمع والطرح كما في الأمثلة الآتية: print (3+3) >> 6 print (6-4) >> 2 يمكن أيضًا طباعة عمليات الضرب والقسمة وباقي القسمة، إذ يمكنك عبر كتابة الشيفرة التالية طباعة حاصل ضرب عددين مثلًا: print (6*6) >> 36 والأمر بسيط كذلك لطباعة حاصل قسمة عددين: print (49/7) >> 7.0 أما حالة طباعة باقي القسمة، فسنستخدِم رمز النسبة المئوية %: print (50%7) >> 1 كما يمكنك طباعة أيّ عدد من النصوص باستخدام علامة الزائد + كما في المثال الآتي: print ("Hello " + "World!") >> Hello World! إذا أردنا تضمين قيمة عددية داخل نص، فيجب فيجب تحويل العدد إلى نص لأنّ الأعداد في بايثون وفي عدد من اللغات الأخرى عمومًا هي نوع من أنواع البيانات، لذلك يجب استخدام دالة str في هذه الحالة لتحويل العدد إلى نص لتستطيع لغة بايثون التعامل معها على أساس نص. print ("Hello "+str(2)+"nd "+"World!") >> Hello 2nd World! نستطيع الاستغناء عن دالة str إذا أدرجنا الأعداد داخل علامات التنصيص، إذ ستَعُدّ بايثون الأعداد داخل علامات التنصيص نصًا عاديًا، كما سنتعرف على الدوال بصورة أكبر في الصفحات القادمة. print ("Hello " + "2nd " + "World!") >> Hello 2nd World! المعرفات في بايثون تعد المعرِّفات Identifiers من أساسيات البايثون وهي الكلمات التي تستطيع من خلالها تعريف اسم متغير Variable أو ثابت Constant أو دالة Function أو صنف Class أو وحدة Module، ونختصر ذلك كله الآن بكلمة المعرِّفات فقط للدلالة على أيّ منهم، فتلك المتغيرات والثوابت والدوال والأصناف والوحدات، كلها مواضيع وسنتعرف عليها بالتفصيل في هذا الكتاب. تخضع تسمية المعرفات إلى قواعد محدَّدة لا يُمكن الخروج عنها في بايثون؛ لأنه عند الخروج عن إحدى القواعد سيطبع مفسر بايثون رسالة خطأ عند محاولتك تشغيل البرنامج، وتلك القواعد كما يلي: يجب أن تبدأ بحرف إنجليزي أو شَرطة سفلية Underscore _ ولا يُمكنها البدء برقم أبدًا. لا يمكنها احتواء رموز مثل % أو $ أو & …إلخ، ولكن يمكنها احتواء الأرقام. ألا تكون مطابقةً لأيّ كلمة من الكلمات المفتاحية في بايثون. الكلمات المفتاحية في بايثون هي كلمات تُستخدَم في أصل اللغة، بمعنى أنّ تلك الكلمات يقرؤها مفسر لغة بايثون لإجراء مهمة ما، فكلمة مثل print كما علمنا من قبل تؤدي مهمة طباعة نص أو عدد على الشاشة، لذلك لا نستطيع تعريف متغير أو ثابت أو دالة بهذا الاسم، وتكون جميع الكلمات المفتاحية في لغة بايثون كما يلي: table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } and exec not as finally or assert for Pass break from print class global raise continue if return def import try del in while elif is with else lambda yield except والجدير بالذكر أنّ جميع تلك الكلمات المفتاحية لا تحتوي على أي حرف كبير، وبما أنّ لغة بايثون لغة حساسة لحالة الأحرف، فمن تسمية أيّ معرِّف بتلك الكلمات في حالة تغيير حالة الأحرف، بمعنى أنه لا يُمكنك تسمية متغير باسم print، لكن يمكنك تسمية متغير باسم Print أو PRINT لأنه بالنسبة لبايثون، تكون الكلمات الثلاثة السابقة مختلفةً تمامًا عن بعضها، وبناءً على ذلك، فإذا أردت تسمية متغير أو دالة أو صنف في بايثون، فيجب عليك اتباع القواعد السابقة. كما أنّ هناك أيضًا في بايثون بعض التوصيات أثناء التسمية، وهي ليست قواعدًا يجب اتباعها بل هي أقرب إلى العُرف، ولكن من الأفضل اتباعها حتى تكون الشيفرة المصدرية في أفضل صورة ممكنة، وتلك التوصيات هي كما يلي: اسم الصنف من المفضل أن يبدأ بحرف كبير مثل Mouse وليس mouse. جميع المعرِّفات الأخرى مثل المتغيرات والدوال من الأفضل أن تبدأ بحرف صغير. إذا عرَّفت متغيرًا ما خاصًا، ولا تريد أن يُستخدَم في أيّ مكان آخر في البرنامج، فمن المفضل بدء اسم المتغير بشرطة سفلية واحدة أو شرطتين إذا كان المتغير خاصًا جدًا. السطور والمسافات لدى بايثون نوعًا فريدًا من القواعد عندما يتعلق الأمر بالأسطر والمسافات وتنظيم الشيفرة، إذ تستخدِم معظم لغات البرمجة الأخرى الأقواس المعقوصة { } لكتابة كتلة من الشيفرات، لكن الأمر في بايثون مختلف قليلًا، إذ تُنظَّم كتل الشيفرات باستخدام المسافات، وهي عادةً مسافة جدولة Tab، أو فراغين أو أربعة فراغات spaces، فنكتب كتلةً من الشيفرات لتُنفَّذ عند تحقق شرط معين بالشكل التالي: if something_happens: do_this() else: do_that() نقول للمفسر في المثال السابق الوهمي إذا حدث هذا الأمر، افعل هذا؛ وإذ لم يحدث، افعل ذاك، فالأمر بسيط للغاية، المهم دقق بالمسافات وكيف أن do_this تدخل ضمن الشرط if أما do_that فتدخل ضمن else. وبالمثل، عند كتابة كتلة من الشيفرة داخل أيّ قاعدة شرطية أو غيرها من القواعد، يجب عليك البدء في كتابة أسطر الشيفرة الخاصة بالكتلة بعد مسافة تفصل بينها وبين أول السطر، أو بمعنى أدق تفصل بينها وبين كتلة الشيفرة التي تسبقها، انظر مثلًا: count = 10 if count >= 10: if count <= 20: print ("Count is between 10 and 20") else: print ("Count is larger than 20") else: print ("Count is less than 10") وإليك المثال التالي لموازنة طريقة بايثون تلك مع اللغات الأخرى مثل لغة جافاسكربت: let count = 10; if (count <= 20) { if (count <= 20) { console.log("Count is between 10 and 20"); } else { console.log("Count is larger than 20"); } } else { console.log("Count is less than 10"); } تضمَّن كتل الشيفرة في هذا المثال بداخل القواعد الشرطية داخل أقواس معقوصة بغض النظر عن المسافة بين كل سطر وبدايته، المثال السابق يكافئ: let count = 10; if (count <= 20) { if (count <= 20) { console.log("Count is between 10 and 20"); } else { console.log("Count is larger than 20"); } } else { console.log("Count is less than 10"); } المثالان السابقان متطابقان تمامًا ويعملان بلا مشكلة؛ أما في بايثون، فتخضع الشيفرة إلى قاعدة مسافات الأسطر تلك، ولا يُمكن كسر تلك القاعدة، كما أنّ كافة مبرمجي بايثون يحبون قاعدة المسافات لأنها تُجبِر المبرمجين على كتابة شيفرة بسيطة ومنظمة وسهلة القراءة. ومن أساسيات البايثون وأهم القواعد الخاصة بالأسطر في بايثون، نجد قاعدة استكمال الأسطر، فإذا كنت تكتب سطرًا ما وكان هذا السطر طويلًا للغاية وتريد تقسيمه إلى سطرين، فستستطيع في بايثون تحقيق ذلك عبر استخدام الرمز \، فإذا كتبت سطرًا يجمع رقمَين مثلًا ثم يقسم أحدهما على الآخر، وكنت تريد تجزئة هذا السطر إلى سطرين مختلفين، فيمكنك فعل ذلك كما في المثال التالي: 30 + 6 / 6 >> 31.0 30 + 6 \ / 6 >> 31.0 كتبنا في السطر الأول عملية الجمع مع عملية القسمة في سطر واحد مباشرةً وأنتج ذلك العدد 31، أما في المثال الثاني كتبنا عملية الجمع في السطر الأول فقط، ثم ألحقنا عملية الجمع بالرمز \، واستكملنا السطر التالي العمليات بكتابة عملية القسمة مباشرةً، وأنتج ذلك في النهاية العدد نفسه، لكن توجد مع ذلك حالة خاصة لأيّ سطر في بايثون يحتوي على إحدى الأقواس بمختلف أنواعها، مثل [] أو {} أو ()، إذ يمكن كتابة تلك الأسطر على سطرين أو أكثر دون استخدام الرمز السابق ذكره، فالسطر التالي مثلًا يعمل بدون مشكلة حتى مع عدم استخدامنا للرمز. items = ['item_1', 'item_2', 'item_3', 'item_4', 'item_5', 'item_6'] علامات التنصيص توجد اختلافات بسيطة بين علامات التنصيص في لغة بايثون مثل أي لغة برمجة أخرى، إذ يمكنك في بايثون تضمين أيّ نص بين علامتَي تنصيص مُفرَدة ' أو علامتي تنصيص مزدوجة " أو علامتي تنصيص ثلاثية """ أو ''' لكن من أهم قواعد استخدام علامات التنصيص في بايثون هي إنهاء النص بعلامة التنصيص نفسها المُستخدَمة في البداية، فلا يُمكن استخدام علامة تنصيص مُفرَدة في بداية النص ثم استخدام علامة تنصيص مزدوجة في نهايته، وإنما يجب تطابق العلامة في البداية والنهاية للنص الواحد، وإليك أمثلةً على ذلك في أوامر الطباعة: print ("Hello World!") print ('Hello World!') print ("""Hello World!""") أما بالنسبة لعلامات التنصيص الثلاثية، فتُستخدَم في الغالب لامتلاكها ميزةً غير موجودة في العلامات المفرَدة والمزدوجة، إذ تُعَدّ قادرةً على معالجة النصوص التي تتكون من أكثر من سطر، في حين أنّ علامات التنصيص المُفردة والمزدوجة يجب احتوائها على نص مكوَّن من سطر واحد لا أكثر، فلا يمكنك مثلًا طباعة سطرين متتالين عبر علامات التنصيص المزدوجة، أي لا يمكنك تنفيذ المثال التالي في بايثون بصورة صحيحة: print ("Hello World!") لكن يمكنك تنفيذ ذلك الأمر عند استخدام علامات التنصيص الثلاثية كما يلي: print ("""Hello World!""") >> Hello World! التعليقات في أساسيات لغة بايثون ستحتاج في كثير من الأوقات أثناء كتابتك أو عملك على برنامج ما، إلى كتابة بعض الملاحظات على بعض الأسطر، فقد تكون تلك الملاحظات موجهةً لتذكيرك بكيفية عمل هذه الكتلة من الشيفرة، أو لتذكيرك بأمر ما تريد استكماله في هذه الأسطر في وقت لاحق، وتوفر بايثون مثلها مثل بقية لغات البرمجة إمكانية كتابة التعليقات عبر استعمال الرمز # والذي سيؤدي إلى تجاهل ما يليه حتى آخر السطر، انظر مثلًا الشيفرة التالية: # هذا تعليق print ("Hello, Python!") # تعليق آخر >> Hello, Python! كما ترى، فإنّ المثال السابق يعمل عملًا عاديًا وكأنه مكتوب بالشكل التالي دون تعليقات: print ("Hello, Python!") >> Hello, Python! فإذا كتبت أيّ سطر برمجي بعد رمز # فسيتجاهله مفسر بايثون تمامًا كما لو أنه غير موجود؛ وذلك لأن التعليقات هي في الأساس جمل من اللغة الطبيعية البشرية التي يستخدمها المبرمج للتعليق وكتابة الملاحظات بين تعليمات البرنامج. كما أنه في بايثون توجد طريقة أخرى لكتابة التعليقات في أسطر عدة، وذلك باستخدام علامات التنصيص الثلاثية، ومن مميزات هذه الطريقة أنها قد تُستخدم في توثيق الدوال وغير ذلك، كما أن هنالك أدوات تستخلص تلك التعليقات لتوليد توثيق لشيفرة البرنامج ووظائفه. """This is a multi line comment. It's wonderful how easy is Python language!""" print ("Hello World!") تُستخدَم التعليقات استخدامًا كبيرًا أثناء برمجة التطبيقات المعقَّدة، فغير أنها تُستخدَم للتعليق على الشيفرة، فإنها قد تكون مفيدةً بصورة كبيرة في تعطيل وتفعيل بعض الأسطر في الشيفرة أثناء عملك على حل مشكلة ما، مثل نسخ سطر ما ووضعه بداخل التعليق حتى يتجاهله البرنامج، ولكي أحتفظ بذلك السطر لاستخدامه لاحقًا. تعليمات متعددة في سطر واحد تنتهي التعليمة البرمجية في السطر في معظم لغات البرمجة مثل لغة PHP أو جافاسكربت JavaScript عن طريق كتابة رمز الفاصلة المنقوطة ; مما يسمح بكتابة أكثر من تعليمة برمجية في سطر واحد؛ أما في بايثون، فينتهي السطر عن طريق بداية سطر جديد، ولكن مع ذلك تدعم بايثون تلك الميزة أيضًا، إذ يمكنك إنهاء التعليمة البرمجية عبر الرمز ; وهو بالإنجليزية Semicolon، وهو أمر اختياري وليس إجباريًا كما في اللغات الأخرى، وبذلك تستطيع كتابة أكثر من تعليمة برمجية في سطر واحد مثل اللغات الأخرى. print ("Hello"); print("World") >> Hello >> World اقرأ أيضًا المقال السابق: البرمجة والخوارزميات والذكاء الاصطناعي المرجع الشامل إلى تعلم لغة بايثون النسخة العربية الكاملة من كتاب البرمجة بلغة بايثون
  2. يُعَدّ بناء لغة برمجة خاصة بك أمرًا ليس بالصعب على عكس ما تتوقع الغالبية الساحقة من الناس، خاصةً إن لم ترفع سقف توقعاتك كثيرًا، بل قد يكون فيه كثير من التعلم النافع أيضًا، والذي نريد الإشارة إليه في هذا المقال هو أنّ بناء لغة ليس ضربًا من السحر والشعوذة بسبب شعورنا بالإحساس الذي ينتاب المرء في مثل هذه الحالات، فقد جربناه بأنفسنا مع بعض الاختراعات البشرية التي رأينا فيها ذكاءً وصنعةً جعلتنا نظن أنه يستحيل علينا فهم طريقة عملها وبنائها، لكن القراءة والتجربة تفك كثيرًا من هذا السحر. سنبني في هذا المقال لغة برمجة سنسميها لغة البيض Egg، حيث ستكون صغيرةً وبسيطةً لكنها قوية بما يكفي لتعبِّر عن أيّ حسابات تفكر فيها، كما ستسمح بالتجريدات abstractions البسيطة المبنية على دوال. التحليل إن أول ما تراه عينك عند النظر إلى لغة برمجة هو بنيتها اللغوية syntax أو صيغتها notation، والمحلل parser هو برنامج يقرأ نصًا ما وينتج هيكل بيانات data structure، بحيث يعكس بنية البرنامج الموجود في ذلك النص، فإن لم يشكِّل النص برنامجًا صالحًا، فيجب أن يخبرنا المحلل بالخطأ الذي سبب ذلك. ستكون لغتنا ذات بنية لغوية بسيطة وموحدة، حيث سيكون كل شيء في Egg تعبيرًا، وقد يكون التعبير expresion اسم رابطة binding أو عددًا أو سلسلةً نصيةً string أو حتى تطبيقًا، كما تُستخدَم التطبيقات لاستدعاءات الدوال وللبنى اللغوية مثل if أو while. لن تَدعم السلاسل النصية في Egg أيّ شيء يشبه تهريب الشَرطة المائلة العكسية \، ذلك أنّ السلسلة النصية ببساطة هي سلسلة من محارف داخل اقتباسات مزدوجة، لكن لن تكون هي ذاتها اقتباسات مزدوجة، كما سيكون العدد سلسلةً من الأرقام، ويمكن أن تتكون أسماء الرابطات من أيّ محرف ليس مسافةً فارغةً وليس له معنى خاص في البنية التركيبية للغة. تُكتَب التطبيقات بالطريقة نفسها المكتوبة بها في جافاسكربت، وذلك بوضع أقواس بعد التعبير، ويمكنها امتلاك أيّ عدد من الوسائط arguments بين هذين القوسين مفصول بين كل منها بفاصلة أجنبية ,. do(define(x, 10), if(>(x, 5), print("large"), print("small"))) يعني توحيد لغة Egg أنّ الأشياء التي تُرى على أساس عوامل operators في جافاسكربت -مثل ‎>‎- هي رابطات عادية في هذه اللغة، وتُطبَّق مثل أيّ دالة أخرى، كما نحتاج إلى بنية do للتعبير عن تنفيذ عدة أشياء في تسلسل واحد، وذلك لعدم وجود مبدأ الكتل blocks في البنية التركيبية للغة. يتكون هيكل البيانات الذي سيستخدمه المحلل لوصف برنامج ما من كائنات تعابير، لكل منها خاصية type توضِّح نوع التعبير وخصائص أخرى تصف محتواه. تمثِّل التعابير التي من النوع "value" سلاسلًا نصيةً مجردةً أو أعدادًا، وتحتوي خاصية value لها على السلسلة النصية أو قيمة العدد الذي تمثله؛ أما التعابير التي من نوع "word" فتُستخدَم للمعرِّفات -أي الأسماء-، فمثل تلك الكائنات لها خاصية name تحمل اسم المعرِّف على أساس سلسلة نصية، وأخيرًا تمثِّل تعابير "apply" التطبيقات، إذ تملك خاصية operator التي تشير مرجعيًا إلى تعبير يُطَبَّق، بالإضافة إلى خاصية args التي تحمل مصفوفةً من تعابير الوسائط argument expressions. سيُمثَّل جزء ‎>(x, 5)‎ من البرنامج السابق كما يلي: { type: "apply", operator: {type: "word", name: ">"}, args: [ {type: "word", name: "x"}, {type: "value", value: 5} ] } تُسمى مثل هذه الهياكل من البيانات باسم شجرة البُنى syntax tree، فإذا تخيلنا الكائنات نقاطًا والروابط التي بينها خطوطًا بين هذه النقاط، فسيكون لدينا شكلًا يشبه الشجرة، فكما أنّ الشجرة بها أغصان تنقسم ويحمل كل منها أغصانًا أخرى؛ فبدوره يشبه احتواء التعابير على تعابير أخرى تحتوي بدورها على تعابير جديدة، وهكذا. هذا على عكس المحلل الذي كتبناه لصيغة ملف الإعدادات في مقال التعابير النمطية Regular Expressions في جافاسكريبت، والذي كانت بنيته بسيطةً نوعًا ما، إذ يُقسِّم الدخل إلى أسطر ويعالج هذه الأسطر واحدًا واحدًا، حيث فلم يكن ممكنًا للسطر الواحد إلا بضع صور بسيطة يمكنه أن يكون عليها؛ أما هنا فيجب إيجاد طريقة أخرى، فالتعابير ليست مقسمةً إلى أسطر، كما تملك بنيةً تعاوديةً recursive، وتحتوي تعابير التطبيقات على تعابير أخرى. يمكن حل هذه المشكلة بسهولة من خلال كتابة دالة محلل تعاودية على النحو الذي يعكس الطبيعة التعاودية للغة. نعرِّف الدالة parseExpression التي تأخذ سلسلةً نصيةً على أساس دخل لها، وتُعيد كائنًا يحتوي هيكل البيانات للتعبير في بداية السلسلة النصية مع الجزء المتبقي من السلسلة النصية بعد تحليل هذا التعبير، ويمكن استدعاء هذه الدالة مرةً أخرى حين نحلل التعابير الفرعية كما في حالة وسيط التطبيق مثلًا، وذلك لنحصل على التعبير الوسيط بالإضافة إلى النص المتبقي، وقد يحتوي هذا النص بدوره على وسائط أخرى، أو قد يكون هو قوس الإغلاق في نهاية قائمة الوسائط. يكون أول جزء في المحلل كالتالي: function parseExpression(program) { program = skipSpace(program); let match, expr; if (match = /^"([^"]*)"/.exec(program)) { expr = {type: "value", value: match[1]}; } else if (match = /^\d+\b/.exec(program)) { expr = {type: "value", value: Number(match[0])}; } else if (match = /^[^\s(),#"]+/.exec(program)) { expr = {type: "word", name: match[0]}; } else { throw new SyntaxError("Unexpected syntax: " + program); } return parseApply(expr, program.slice(match[0].length)); } function skipSpace(string) { let first = string.search(/\S/); if (first == -1) return ""; return string.slice(first); } يجب قص المسافات الفارغة من بداية السلسلة النصية للبرنامج بما أنّ لغة Egg التي نكتبها تشبه جافاسكربت في كونها تسمح لأيّ عدد من المسافات الفارغة بين عناصرها، وهنا يأتي دور دالة skipSpace. تَستخدم parseExpression بعد تخطي أي مسافة بادئة ثلاثة تعبيرات نمطية لتحديد العناصر الصغرى الثلاثة التي تدعمها Egg، وهي السلاسل والأعداد والكلمات، كما يبني المحلل نوعًا مختلفًا من هياكل البيانات وفقًا للتي تطابق منها، فإذا كان الإدخال لا يتطابق مع أحد هذه النماذج الثلاثة، فلا يكون تعبيرًا صالحًا ويرفع المحلل خطأً. نحن نستخدم SyntaxError بدلًا من Error على أساس باني اعتراضات exception، وهو نوع قياسي آخر للأخطاء لأنه أكثر تحديدًا، وهو أيضًا نوع الخطأ الذي يرمى عند محاولة تشغيل برنامج JavaScript غير صالح، ثم نقص الجزء المطابق من سلسلة البرنامج النصية ونمرره مع كائن التعبير إلى parseApply الذي ينظر هل التعبير تطبيق أم لا، فإذا كان تطبيقًا، فسيحلَِل قائمةً من الوسائط محصورةً بين قوسين. function parseApply(expr, program) { program = skipSpace(program); if (program[0] != "(") { return {expr: expr, rest: program}; } program = skipSpace(program.slice(1)); expr = {type: "apply", operator: expr, args: []}; while (program[0] != ")") { let arg = parseExpression(program); expr.args.push(arg.expr); program = skipSpace(arg.rest); if (program[0] == ",") { program = skipSpace(program.slice(1)); } else if (program[0] != ")") { throw new SyntaxError("Expected ',' or ')'"); } } return parseApply(expr, program.slice(1)); } إذا كان المحرف التالي في البرنامج ليس قوسًا بادئًا ) فلن يكون هذا تطبيقًا، وتُعيد parseApply التعبير المعطى لها، وإلا فستتخطى القوس البادئ وتنشئ كائن شجرة بُنى syntax tree object لتعبير التطبيق ذاك، ثم تستدعي parseExpression تعاوديًا لتحلِّل كل وسيط حتى تجد القوس الغالق، كما يكون التعاود هنا غير مباشر من خلال استدعاء parseApply لدالة parseExpression والعكس، ولأن تعبير التطبيق يمكن تطبيقه كما في multiplier(2)(1)‎، فيجب أن تستدعي parseApply نفسها مرةً أخرى لتنظر فيما إذا كان يوجد زوج آخر من الأقواس أم لا، وذلك بعد تحليل تطبيق ما. هذا كل ما نحتاجه لتحليل لغة Egg، إذ نغلفها في دالة parse لتتحقق أنها وصلت إلى نهاية سلسلة الدخل بعد تحليل التعبير -فبرنامج بلغة Egg هو تعبير وحيد-، ثم تعطينا هيكل البيانات للبرنامج. function parse(program) { let {expr, rest} = parseExpression(program); if (skipSpace(rest).length > 0) { throw new SyntaxError("Unexpected text after program"); } return expr; } console.log(parse("+(a, 10)")); // → {type: "apply", // operator: {type: "word", name: "+"}, // args: [{type: "word", name: "a"}, // {type: "value", value: 10}]} وهكذا تعمل اللغة بنجاح، رغم أنها لا تعطينا معلومات مفيدة حين تفشل أو تتعطل، كما لا تخزِّن السطر والعمود الذي يبدأ عنده كل تعبير -وهو الأمر الذي قد يكون مفيدًا عند الإبلاغ عن الأخطاء فيما بعد-، لكن لا يهم، فما أنجزناه إلى الآن كافي. المقيم لا شك في أننا نريد شجرة بنى لبرنامج ما من أجل تشغيلها، وهذه هي وظيفة المقيِّم evaluator، إذ تعطيه شجرة بنى وكائن نطاق يربط الأسماء بالقيم، وسيقيِّم التعبير الذي تمثله الشجرة، ثم يُعيد القيمة التي يخرجها. const specialForms = Object.create(null); function evaluate(expr, scope) { if (expr.type == "value") { return expr.value; } else if (expr.type == "word") { if (expr.name in scope) { return scope[expr.name]; } else { throw new ReferenceError( `Undefined binding: ${expr.name}`); } } else if (expr.type == "apply") { let {operator, args} = expr; if (operator.type == "word" && operator.name in specialForms) { return specialForms[operator.name](expr.args, scope); } else { let op = evaluate(operator, scope); if (typeof op == "function") { return op(...args.map(arg => evaluate(arg, scope))); } else { throw new TypeError("Applying a non-function."); } } } } يحتوي المقيِّم على شيفرة لكل نوع من أنواع التعابير، ويُخرج لنا تعبير القيمة مصنفة النوع literal value expression قيمته، كما في حالة التعبير 100 الذي يقيِّم إلى العدد 100 فقط؛ أما في حالة الرابطة، فيجب التحقق مما إذا كانت معرَّفةً على الحقيقة في النطاق أم لا، وإن كانت فسنجلب قيمتها. تُعَدّ التطبيقات أكثر تفصيلًا من ذلك، فإذا كانت صيغةً خاصةً مثل if، فلا نقيِّم أيّ شيء ونمرِّر التعابير الوسيطة مع النطاق إلى الدالة التي تعالج هذه الصيغة؛ أما إن كانت استدعاءً عاديًا، فسنقيِّم العامِل ونتحقق أنه دالة، ونستدعيها مع الوسائط المقيَّمة. نستخدِم قيمًا لدوال جافاسكربت عادية لنمثِّل قيم الدوال في Egg، كما سنعود إلى هذا بعد قليل حين تُعرَّف الصيغة الخاصة المسماة fun، ويمثِّل الهيكل التعاودي لـ evaluate هيكلًا مماثلًا للمحلِّل، وكلاهما يعكس هيكل اللغة نفسه، ومن الممكن مكاملة المحلِّل والمقيِّم أثناء التحليل أيضًا، لكن فصلهما عن بعضهما البعض بهذه الطريقة يجعل البرنامج أكثر نقاءً. هذا جل ما نحتاج إليه لتفسير لغة Egg ببساطة، لكن من ناحية أخرى، لا تستطيع فعل الكثير بهذه اللغة دون تعريف بعض الصيغ الخاصة الأخرى وإضافة بعض القيم المفيدة إلى البيئة. الصيغ الخاصة يُستخدَم كائن الصيغ الخاصة specialForms لتعريف البُنى الخاصة في لغة Egg، حيث يربط الكلمات بالدوال التي تقيِّم مثل تلك الصيغ، ولنضف if بما أنه فارغ الآن: specialForms.if = (args, scope) => { if (args.length != 3) { throw new SyntaxError("Wrong number of args to if"); } else if (evaluate(args[0], scope) !== false) { return evaluate(args[1], scope); } else { return evaluate(args[2], scope); } }; تتوقع بنية if ثلاثة وسائط بالضبط، وستقيّم الأول منها، فإذا لم تكن النتيجة هي القيمة false فستقيِّم الثاني، وإلا فالثالث هو الذي يُقيَّم، وتُعَد صيغة if هذه أقرب إلى عامل ‎?:‎ الثلاثي في جافاسكربت منها إلى عامل if في جافاسكربت أيضًا، فهو تعبير وليس تعليمة، ويُنتِج قيمةً تكون نتيجة الوسيط الثاني أو الثالث. تختلف كذلك لغة Egg عن جافاسكربت في كيفية معالجة القيمة الشرطية إلى if، فهي لا تعامل الصفر والسلسلة النصية الفارغة على أنها خطأ false، بل القيمة false حصرًا وفقط. السبب الذي يجعلنا في حاجة إلى تمثيل if على أساس صيغة خاصة بدلًا من دالة عادية، هو أن جميع وسائط الدوال تُقيَّم قبل استدعاء الدالة، بينما يجب ألا تقيِّم if إلا وسيطها الثاني أو الثالث بناءً على قيمة الوسيط الأول. صيغة while شبيهة بهذا، فبما أن undefined غير موجودة في لغة Egg، فسنُعيد false لعدم وجود نتيجة أفضل وأكثر فائدة، كما في المثال التالي: specialForms.while = (args, scope) => { if (args.length != 2) { throw new SyntaxError("Wrong number of args to while"); } while (evaluate(args[0], scope) !== false) { evaluate(args[1], scope); } return false; }; كذلك لدينا وحدة بنيوية أساسية أخرى هي do التي تنفِّذ كل وسائطها من الأعلى إلى الأسفل، وتكون قيمتها هي القيمة التي ينتجها الوسيط الأخير. specialForms.do = (args, scope) => { let value = false; for (let arg of args) { value = evaluate(arg, scope); } return value; }; ننشئ صيغةً نسميها define كي نستطيع إنشاء رابطات ونعطيها قيمًا جديدةً، حيث تتوقع هذه الصيغة للوسيط الأول لها أن يكون كلمةً وتعبيرًا ينتج القيمة التي سنسدها إلى تلك الكلمة لتكون الوسيط الثاني، وبما أنّ define ما هي إلا تعبير، فيجب أن تُعيد قيمةً ما، وسنجعلها تُعيد القيمة التي أُسندت إليها كما في حالة عامل = في جافاسكربت. specialForms.define = (args, scope) => { if (args.length != 2 || args[0].type != "word") { throw new SyntaxError("Incorrect use of define"); } let value = evaluate(args[1], scope); scope[args[0].name] = value; return value; }; البيئة يكون النطاق الذي تقبَله evaluate كائنًا له خصائص تتوافق أسماؤها مع أسماء الرابطات، كما تتوافق قيمها مع القيم التي ترتبط بهذه الرابطات، وسنعرِّف كائنًا ليمثل النطاق العام. يجب أن تكون لنا وصول إلى قيم بوليانية كي نستطيع استخدام بنية if التي عرفناها لتونا، وبما أنه لا يوجد إلا قيمتان منطقيتان فقط، فلا نحتاج إلى ترميز خاص لهما، بل نربطهما بالقيمتين true وfalse ثم نستخدمهما. const topScope = Object.create(null); topScope.true = true; topScope.false = false; نستطيع الآن تقييم تعبير بسيط يرفض قيمةً بوليانيةً. let prog = parse(`if(true, false, true)`); console.log(evaluate(prog, topScope)); // → false سنضيف بعض قيم الدوال إلى النطاق لتوفير العوامل الحسابية الأساسية وكذلك عوامل الموازنة، كما سنستخدم Function -بهدف تبسيط الشيفرة والحفاظ على قصرها- لتوليف مجموعة من دوال العوامل في حلقة تكرارية بدلًا من تعريف كل واحد منها على حدة. for (let op of ["+", "-", "*", "/", "==", "<", ">"]) { topScope[op] = Function("a, b", `return a ${op} b;`); } إذا كانت لدينا طريقة لإخراج القيم، فسيكون ذلك مفيدًا لنا بلا شك، لذا سنغلِّف console.log في دالة ونسميها print. topScope.print = value => { console.log(value); return value; }; يعطينا هذا الأدوات الأساسية اللازمة لكتابة برامج بسيطة، حيث توفر الدالة أدناه طريقةً سهلةً لتحليل برنامج ما وتشغيله في نطاق جديد: function run(program) { return evaluate(parse(program), Object.create(topScope)); } سنستخدم سلاسل كائن النموذج الأولي لتمثيل النطاقات المتشعِّبة nested scopes كي يتمكن البرنامج من إضافة روابط إلى نطاقه المحلي دون تغيير النطاق الأعلى top-level scope. run(` do(define(total, 0), define(count, 1), while(<(count, 11), do(define(total, +(total, count)), define(count, +(count, 1)))), print(total)) `); // → 55 ذلك هو البرنامج الذي رأيناه عدة مرات من قبل، والذي يحسب مجموع الأرقام من 1 إلى 10 مكتوبًا بلغة Egg، ومن الواضح أنه ليس أجمل من مثيله في جافاسكربت، لكنا نراه ممتازًا إذا نظرنا إلى حقيقة أنّ اللغة التي كُتب بها ليس فيها إلا 150 سطرًا من الشيفرات فقط. الدوال تُعَدّ لغة البرمجة التي ليس فيها دوال لغةً فقيرةً في إمكاناتها، لكن لحسن الحظ فليس من الصعب إضافة بنية مثل fun التي تعامِل وسيطها الأخير على أنه متن الدالة، وستستخدم جميع الوسائط التي قبله على أساس أسماء لمعامِلات الدالة. specialForms.fun = (args, scope) => { if (!args.length) { throw new SyntaxError("Functions need a body"); } let body = args[args.length - 1]; let params = args.slice(0, args.length - 1).map(expr => { if (expr.type != "word") { throw new SyntaxError("Parameter names must be words"); } return expr.name; }); return function() { if (arguments.length != params.length) { throw new TypeError("Wrong number of arguments"); } let localScope = Object.create(scope); for (let i = 0; i < arguments.length; i++) { localScope[params[i]] = arguments[i]; } return evaluate(body, localScope); }; }; تحصل الدوال في لغة Egg على نطاقاتها المحلية الخاصة بها، إذ تُنشِئ الدالة المنتَجة بواسطة صيغة fun هذا النطاق المحلي، وتضيف الرابطات الوسيطة إليه، ثم تقيِّم متن الدالة في هذا النطاق وتُعيد النتيجة. run(` do(define(plusOne, fun(a, +(a, 1))), print(plusOne(10))) `); // → 11 run(` do(define(pow, fun(base, exp, if(==(exp, 0), 1, *(base, pow(base, -(exp, 1)))))), print(pow(2, 10))) `); // → 1024 التصريف يسمى الذي بنيناه حتى الآن بالمفسِّر interpreter، والذي يتصرف مباشرةً أثناء التقييم على تمثيل البرنامج الذي أنتجه المحلِّل؛ أما التصريف compilation، فهو عملية إضافة خطوة أخرى بين التحليل وتشغيل البرنامج، إذ تحوِّل البرنامج إلى شيء يمكن تقييمه بكفاءة أكبر من خلال إضافة كل ما يمكن إضافته من عمل ومهام مقدمًا، فمن الواضح في اللغات متقَنة التصميم مثلًا استخدام كل رابطة وأيّ رابطة مشار إليها بدون تشغيل البرنامج فعليًا، حيث يُستخدَم هذا لتجنب البحث عن الرابطة باسمها في كل مرة يوصَل إليها، بدلًا من جلبها مباشرةً من موقع محدد سلفًا في الذاكرة. يتضمن التصريف عادةً تحويل البرنامج إلى لغة الآلة، وهي الصيغة الخام التي يستطيع معالج الحاسوب تنفيذها، لكن هذه ليست الصورة الوحيدة له، فأيّ عملية تحوِّل البرنامج إلى تمثيل آخر مختلف يمكن النظر إليها على أنها تصريف كذلك. سيكون من الممكن كتابة خطة تقييم بديلة للغة Egg، إذ تبدأ بتحويل البرنامج إلى برنامج جافاسكربت أولًا، ثم تستخدِم Function لاستدعاء مصرِّف جافاسكربت له، وبعدها سنحصل على النتيجة، فإذا كان هذا بكفاءة، فسيجعل لغة Egg تعمل بمعدل سريع جدًا مع الحفاظ على بساطة الاستخدام. الاحتيال لعلك لاحظت حين عرَّفنا if وwhile على أنهما ليستا سوى تغليف بسيط نوعًا ما حول نظيرتيهما في جافاسكربت، وبالمثل، فإنّ القيم في Egg ما هي إلا قيم جافاسكربت العادية. إذا وازنت استخدام Egg المبني على جافاسكربت مع كمية الجهد والتعقيد المطلوب لبناء لغة برمجة من الوظائف المجردة الخام التي يوفرها الحاسوب على أساس عتاد، فإنّ الفارق عظيم، وعلى كل حال يعطيك هذا المثال صورةً للطريقة التي تعمل بها لغة البرمجة. إذا وضعنا هذا في ميزان الإنجاز، فستكون الاستفادة من العجلة الموجودة أفضل من إعادة اختراعها وتنفيذ كل شيء بأنفسنا، رغم أنّ هذه اللغة التي أنشأناها في هذا المقال والتي تشبه لعب الأطفال لا تفعل أي شيء لا تفعله جافاسكربت بالكفاءة نفسها إن لم يكن أفضل، إلا أن هناك حالات سيكون من المفيد فيها كتابة برامج بسيطة لإنجاز المهام التي بين أيدينا، ومثل هذه اللغة ليس عليها أن تكون على صورة اللغة التقليدية، فإذا لم تأتي جافاسكربت بتعابير نمطية مثلًا، فستستطيع كتابة محلِّل خاص بك ومقيِّم كذلك من أجل هذه التعابير؛ أو لنقل أنك تبني ديناصورًا آليًا عملاقًا وتريد برمجة سلوكه، فقد لا تكون جافاسكربت حينها هي اللغة المثلى لذلك، وستجد أنك تحتاج إلى لغة تبدو هكذا: behavior walk perform when destination ahead actions move left-foot move right-foot behavior attack perform when Godzilla in-view actions fire laser-eyes launch arm-rockets يُطلَق على مثل هذه اللغة أنها لغة مختصة بمجال بعينه domain-specific، وهي لغة مفصَّلة لتعبِّر عن عناصر مجال معين من المعرفة، ولا تتعداه إلى غيره، كما تكون أكثر وصفًا وإفصاحًا عن اللغة الموجَّهة للأغراض العامة لأنها مصمَّمة لتصف الأشياء المراد وصفها حصرًا في هذا المجال وحسب. تدريبات المصفوفات اجعل لغة Egg تدعم المصفوفات من خلال إضافة الدوال الثلاثة التالية إلى النطاق العلوي top scope: array(...values)‎ لبناء مصفوفة تحتوي على قيم وسيطة. length(array)‎ للحصول على طول مصفوفة ما. element(array, n)‎ لجلب العنصر رقم n من مصفوفة ما. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. // عدِّل هذه التعريفات... topScope.array = "..."; topScope.length = "..."; topScope.element = "..."; run(` do(define(sum, fun(array, do(define(i, 0), define(sum, 0), while(<(i, length(array)), do(define(sum, +(sum, element(array, i))), define(i, +(i, 1)))), sum))), print(sum(array(1, 2, 3)))) `); // → 6 إرشادات للحل إن أسهل طريقة لإضافة الدعم هي تمثيل مصفوفات Egg بمصفوفات جافاسكربت، ويجب أن تكون القيم المضافة إلى النطاق العلوي دوالًا، كما يمكن أن يكون تعريف array بسيطًا جدًا إذا استَخدَمت وسيط rest مع الصيغة ثلاثية النقاط triple-dot notation. التغليف Closure تسمح لنا الطريقة التي عرَّفنا بها fun بإضافة دوال في Egg للإشارة مرجعيًا إلى النطاق المحيط، مما يسمح لمتن الدالة أن تستخدِم قيمًا محليةً كانت مرئيةً في وقت تعريف الدالة، تمامًا مثلما تفعل دوال جافاسكربت، ويوضِّح البرنامج التالي هذا، إذ تُعيد دالة f دالةً تضيف وسيطها إلى وسيط f، مما يعني أنها تحتاج إلى وصول للنطاق المحلي الموجود داخل f كي تستطيع استخدام الرابطة a. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. run(` do(define(f, fun(a, fun(b, +(a, b)))), print(f(4)(5))) `); // → 9 ارجع الآن إلى تعريف صيغة fun واشرح الآلية المسببة لعملها. إرشادات للحل سنستخدِم آليات جافاسكربت مرةً أخرى للحصول على مزايا مشابهة في Egg، حيث تُمرَّر الصيغ الخاصة إلى النطاق المحلي الذي تقيَّم فيه كي تقيِّم هي صيغها الفرعية في ذلك النطاق، ويكون للدالة التي تعيدها fun وصول إلى وسيط scope المعطى للدالة المغلِّفة، كما تستخدِم ذلك لإنشاء نطاق الدالة المحلي حين تُستدعى. يعني هذا أن النموذج الأولي للنطاق المحلي سيكون هو النطاق الذي أُنشَئت فيه الدالة مما يمكننا من الوصول إلى الرابطات التي في ذلك النطاق من خلال الدالة، وهذا كله من أجل استخدام التغليف closure رغم أنك في حاجة إلى مزيد من العمل لتُصرِّف ذلك بكفاءة. التعليقات أليس من الجميل أن تتمكن من كتابة تعليقات في لغة Egg؟ فلو جعلنا التعليق يبدأ بعلامة # مثلًا كما في حال علامتي // في جافاسكربت -ولا تقلق بشأن المحلِّل، إذ لن نجري أيّ تغييرات جوهرية فيه كي يدعم التعليقات-، فما علينا سوى تغيير skipspace كي تتخطى التعليقات كذلك، كما لو كانت مسافات فارغة، بحيث نتخطى التعليقات في كل مرة نستدعي فيها skipspace. الشيفرة أدناه تمثل skipspace، عدِّلها لتضيف دعم التعليقات في لغة Egg، مسترشدًا بما سبق. تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. function skipSpace(string) { let first = string.search(/\S/); if (first == -1) return ""; return string.slice(first); } console.log(parse("# hello\nx")); // → {type: "word", name: "x"} console.log(parse("a # one\n # two\n()")); // → {type: "apply", // operator: {type: "word", name: "a"}, // args: []} إرشادات للحل تأكد من جعل حلّك قادرًا على معالجة التعليقات المتعددة في صف مع مسافات فارغة محتملة بينها أو بعدها. سيكون التعبير النمطي أسهل طريقة تستخدمها في هذا التمرين للحل، لهذا اكتب شيئًا يطابق "مسافةً فارغةً أو تعليقًا أو صفرًا أو أكثر من مرة". واستخدم التابع exec أوmatch، وانظر إلى طول أول عنصر في المصفوفة المعادة -أي التطابق الكامل- لتعرف كم عدد المحارف التي عليك قصها. إيجاد النطاق الطريقة الوحيدة حاليًا لإسناد قيمة إلى رابطة هي define، حيث تتصرف هذه البنية مثل طريقة لتعريف رابطات جديدة وإعطاء قيمة جديدة للرابطات الحالية. لكن هذا الإبهام يجعلك تقع في مشكلة تعريف رابطة محلية بالاسم نفسه في كل مرة تحاول إعطاء رابطة غير محلية قيمةً جديدةً، كما نستنكر هذه الطريقة في معالجة النطاقات رغم وجود لغات مصممة أصلًا على هذا الأساس. أضف صيغة set الخاصة التي تشبه define، وتعطي قيمةً جديدةً للرابطة لتحدِّث الرابطة في نطاق خارجي إذا لم تكن موجودةً سلفًا في النطاق الداخلي، وإن لم تكن هذه الرابطة معرفةً، فأبلغ بالخطأ ReferenceError الذي هو نوع خطأ قياسي. سيعيقك نوعًا ما أسلوب تمثيل النطاقات على أساس كائنات بسيطة من أجل السهولة ها هنا، خاصةً إذا أردت استخدام دالة Object.getPrototypeOf مثلًا التي تعيد النموذج الأولي لكائن ما، وتذكَّر أيضًا أنّ النطاقات لا تنحدر من Object.prototype، لذا فإن أردت استدعاء hasOWnProperty عليها، فعليك استخدام التعبير التالي: Object.prototype.hasOwnProperty.call(scope, name); specialForms.set = (args, scope) => { // ضع شيفرتك هنا. }; run(` do(define(x, 4), define(setx, fun(val, set(x, val))), setx(50), print(x)) `); // → 50 run(`set(quux, true)`); // → ReferenceError أحد صور تستطيع تعديل شيفرة التدريب لكتابة الحل وتشغيلها في طرفية المتصفح إن كنت تقرأ من متصفح، أو بنسخها إلى codepen. إرشادات للحل سيكون عليك التكرار على نطاق واحد في كل مرة باستخدام Object.getPrototypeOf للذهاب إلى النطاق الخارجي اللاحق. واستخدِم hasOwnProperty لتعرف ما إذا كانت الرابطة الموضَّحة بخاصية name في أول وسيط لـ set موجودةً في ذلك النطاق أم لا، فإن كانت كذلك فاضبطها على نتيجة تقييم الوسيط الثاني لـ set ثم أعد تلك القيمة، وإذا وصلت إلى أقصى نطاق خارجي -بحيث تكون إعادة Object.getPrototypeOf هي null- ولم تعثر على الرابطة بعد، فستكون هذه الرابطة غير موجودة ويجب رمي خطأ هنا. ترجمة -بتصرف- للفصل الثاني عشر من كتاب Elequent Javascript لصاحبه Marijn Haverbeke. اقرأ أيضًا المقال السابق: البرمجة غير المتزامنة في جافاسكريبت دليلك الشامل لتعلم البرمجة النسخة الكامة من كتاب مدخل إلى الذكاء الاصطناعي وتعلم الآلة
  3. يتعامل المبرمجون مع شفرة البرامج المصدرية بأسلوبين مختلفين، فالأول يرى أنّ “الشفرة التي تعمل هي الشفرة الجيدة” أما الثاني فيرى أنّه “ما دامت الشفرة جيدة فإنّها ستعمل بكل تأكيد” وبصياغة أخرى: “المهمّ أنها تعمل” مقابل “المهمّ أن تكون صحيحة”. كل يوم تقريبًا أقرأ هذه العبارة وأشباهها في تعليقات المدونة: “ما الحاجة إلى مبادئ البرمجة كائنية التوجه إن كانت الشفرة البرمجية تعمل بشكل جيّد دون استخدامها؟ ما الهدف من إدخال طرق وأساليب جديدة يفترض بها أن تكون أفضل من سابقاتها، إن كانت الطريقة التقليدية الحالية - والتي تتوسط البرمجة الإجرائية وكائنية التوجه - تعمل جيّدًا؟” لنفكّر في الأمر من زاوية مختلفة، وننظر إلى الأمور على نحو أعمّ ونفكّر من ناحية تطوير البرمجيات لا من ناحية البرمجة كائنية التوجه. هناك الكثير من الأمثلة التي تنطبق عليها عقلية “المهمّ أنّه يعمل”. لنأخذ لغة Perl، وهي لغة برمجية تشتهر بقدرتها على القيام بأيّ شيء بثلاث طرق مختلفة، بمعنى أنّه لا وجود لطريقة واحدة صحيحة. لست خبيرًا في Perl، لذا فلنلق نظرة على شفرة Ruby التالية: if a > b m = 'Hello!' end يمكن كتابة الشفرة السابقة بهذه الطريقة أيضًا: m = if a > b 'Hello!' end أو هذه: m = 'Hello!' if a > b وإليك المزيد: m = a > b ? 'Hello' : nil أيّ الشفرات السابقة صحيحة؟ هل يمكن ﻷي مبرمج بلغة Perl أن يخبرنا بذلك؟ هل يمكن اقتراح طرق أخرى للوصول إلى نفس النتيجة؟ أما في لغة Java (وهي لغة أكثر صرامة من Ruby) فليس من المفاجئ أن تكون هناك طريقة واحدة للقيام بذلك: if (a > b) { m = "Hello!"; } أعتقد أنّني أخطأت، فهنالك طريقة ثانية: if (a > b) m = "Hello!"; ما الذي يمكن أن يجنيه المبرمجون من هذا التنوّع الكبير؟ أعتقد أن الإجابة تعتمد على كوننا نكتب الشفرة أم نقرؤها؟ كذلك يعتمد الأمر على موقفنا تجاه البرنامج الذي نعمل على إنشائه، فإما نرى بأنّه ملكنا (عقلية المخترق) أو أنّنا نبنيه وحسب (عقلية المصمّم). إن كنا نكتب الشفرة البرمجية، وكنا نرى أنفسنا أصحاب تلك الشفرة، فسنحتاج إلى ترسانة أسلحة التجميل اللغوي Syntactic sugar لنثبت لأنفسنا بأننا أذكياء، وبالتأكيد لنتباهى أمام أصدقائنا بمفسّر Ruby الكئيب. في المقابل، إن كنا نعدّ أنفسنا في عداد المصممين، فسنصاب بالانزعاج والإحباط عند قراءة شفرة برمجية مليئة بأساليب التجميل اللغوي والتي “تعمل دون مشاكل”. ربما يجب أن أقتصر في الحديث على نفسي، الواقع أنني متأكّد من أنني سأصاب بهذا الشعور. يمكن أن نعدّ صياغة لغة Ruby والتي تتيح هذا القدر الكبير من التجميل اللغوي مثالًا واضحًا على التناقض الحاصل بين مبدأي “المهمّ أن تعمل” و “المهم أن تكون جيدة”. إذ تتمثل فلسفة Ruby في أنّه لا أهمّية لطريقة كتابة الشفرة البرمجية ما دامت تؤدّي عملها المطلوب منها. أما فلسفة Java فمختلفة تمامًا، وهي أقرب ما تكون إلى: اكتب شفرة صحيحة وستعمل بالتأكيد. إضافة إلى ذلك، فإن نوع البيانات الضعيف والديناميكي Weak and dynamic type الذي تتمتع به لغة Ruby مقابل نوع البيانات الصلب والثابت Strong and static type في Java، يعدّ دليلًا آخر عمّا أتحدّث عنه. أرى عموما أنّه كلما زادت مرونة اللغة البرمجية، قلّت قابلية صيانة الشفرة المكتوبة بها، وبمعنى آخر فإن الجودة الأعلى تأتي من اللغات الأبسط. وهذا الأمر ينطبق كذلك على عملية تطوير البرمجيات: فكلما زادت القيود المفروضة على المبرمجين وقلّت الخيارات المتاحة أمامهم في طريقة كتابة الشفرة البرمجية، زادت جودة البرنامج المكتوب بتلك اللغة. تحاول المحلّلات الساكنة Static analyzers مثل Checkstyle في لغة Java أو Rubocop في لغة Ruby حلّ هذه المشكلة وذلك بمنعنا من استخدام خصائص معيّنة في اللغة البرمجية، ولكنّها لا تقدّم نتائج جيّدة لأنّنا مبدعون في كتابة الشفرات بأساليب وأشكال متنوعة. لنعد الآن إلى السؤال الخاص بالبرمجة كائنية التوجه: ما الحاجة إلى تطوير أي شيء إن كان يؤدي عمله بهيئته الحالية؟ الجواب: البرمجة كائنية التوجه الحديثة (كما في Java، Ruby و ++C) لا تنتج شفرة برمجية ذات جودة عالية لأنّها لا تتبع تصوّرا برمجيًّا Paradigm قويًا ومقيّدًا بشكل ملائم. كل ما في الأمر أنّها توفّر الكثير من “الميزات” التي أدخلتها في الغالب ++C وبقيت فيها لأجل منفعتنا المتبادلة. هذه اللغات تعمل بالفعل، ولكنّ قابلية صيانة البرامج التي ننتجها بهذه اللغات تكون منخفضة للغاية. الواقع أن قابليّة الصيانة هذه أقل بكثير ممّا كان سيكون عليه الحال لو كان “إبداعنا” مقيّدًا. ترجمة - وبتصرّف - للمقال Flexibility Equates to Lower Quality لصاحبه Yegor Bugayenko.
×
×
  • أضف...