كيف تُنفَّذ الشفرة
الشفرة التي تكتبها مجرد نص، والمعالج لا يقرأ النصوص؛ فقبل أن يحدث أي شيء يجب ترجمتها إلى صيغة يستطيع الجهاز تنفيذها. وتختلف اللغات في توقيت هذه الترجمة وفي مكان حفظ نتيجتها، وأغلب الأخطاء المربكة تأتي من هذا الاختلاف بالضبط.
- الدرس 2 من 14
- مبتدئ
- مجاني، دون تسجيل
السلسلة التي يمر بها كل سطر شفرة
-
1
النص الذي كتبته
ملف نصي عادي؛ لا يختلف لدى القرص عن ملف ملاحظات.
-
2
فحص النحو
على الملف كله وقبل أي تنفيذ. وإن انكسر هنا لم يُنفَّذ سطر واحد.
-
3
الترجمة إلى صيغة قابلة للتنفيذ
شفرة بايت أو شفرة تشغيلية أو شفرة آلة. تُحفَظ أحيانا على القرص وأحيانا في الذاكرة وأحيانا في لا مكان.
-
4
التنفيذ على المعالج
المكان الوحيد الذي يحدث فيه العمل فعلا، والوحيد الذي يظهر فيه خطأ زمن التنفيذ.
هذه السلسلة مبسّطة. فعمليا توجد طبقات أخرى بين الحلقتين الأخيرتين، من نظام التشغيل إلى تحسينات يجريها محرك اللغة أثناء التنفيذ.
آخر مراجعة: تُراجع الحقائق وأسماء الأدوات في هذا الدرس مقابل مصادرها في هذا التاريخ.
الحاسوب لا يقرأ نص شفرتك؛ فماذا ينفذ إذن؟
الملف الذي تحفظه نص عادي؛ ومن ناحية القرص لا يختلف عن ملف ملاحظات. والمعالج لا يقرأ ذلك النص. فما ينفذه المعالج مجموعة من التعليمات الرقمية البالغة البساطة: خذ هذا العدد من الذاكرة، واجمع عليه ذاك، وضع النتيجة هناك، وإن كانت صفرا فاقفز إلى تلك النقطة. وهذا يُسمى لغة الآلة ولا أحد يعمل بها مباشرة اليوم.
إذن بين نصك وبين المعالج توجد دائما مرحلة ترجمة. لها ثلاث حلقات لا يتغير ترتيبها: النص الذي كتبته، والبرنامج الذي يقرأه ويفحصه ويترجمه، والصيغة المترجمة التي تُنفَّذ في النهاية. ولا تفلت لغة واحدة من هذه السلسلة. وإنما تختلف اللغات في أمرين فقط: متى تحدث الترجمة، وهل تُحفَظ نتيجتها أم لا.
ونقطة تفيدك سريعا جدا: مرحلة الترجمة تفحص النحو قبل كل شيء، أي هل يلتزم نصك بقواعد تلك اللغة أصلا. فإن لم يلتزم توقف العمل عند تلك النقطة ولم يُنفَّذ سطر واحد من برنامجك، ولا حتى سطر كان سليما وسبق موضع الخطأ. والقسم الرابع من هذه الصفحة يعرض ذلك بملفين حقيقيين.
ما الفرق بين الترجمة والتفسير؟
أبسط صياغة: في اللغة المفسَّرة يُسلَّم النص إلى محرك اللغة كما هو ويُنفَّذ من أعلى إلى أسفل؛ وفي اللغة المترجَمة يُحوَّل النص أولا إلى صيغة أخرى وتلك الصيغة هي التي تُنفَّذ. وموزيلا تصوغها بهذه العبارات تماما، إذ تُستلَم الشفرة في اللغات المفسَّرة بصيغتها النصية وتُعالَج منها مباشرة، بينما تُحوَّل اللغات المترجَمة إلى صيغة أخرى قبل تنفيذها.
والآن الجزء المحرج. لم يعد هذا التقسيم خطا واضحا. ففي الصفحة نفسها تضيف موزيلا أن أغلب مفسرات جافاسكريبت الحديثة تستعمل تقنية اسمها الترجمة في الوقت المناسب، فتحوّل المصدر إلى صيغة ثنائية أسرع أثناء استعمال السكربت؛ ومع ذلك تظل تعد جافاسكريبت لغة مفسَّرة، لأن تلك الترجمة تحدث وقت التنفيذ لا قبله.
لذلك بدأ سؤال «أهي مترجَمة أم مفسَّرة» يفقد فائدته. وثمة سؤالان أفضل: متى تحدث الترجمة، وهل تُحفَظ نتيجتها أم تُبنى من جديد في كل مرة؟ وجواب هذين السؤالين يشرح لماذا يحتاج برنامج بلغة سي مرحلة بناء منفصلة قبل التشغيل، ولماذا يبدأ سكربت بايثون الذي يستورد وحدة أسرع قليلا في المرة الثانية.
توقيتان لعمل واحد
كلاهما يترجم. والفرق في توقيت الترجمة، لا في وجودها من عدمه.
ترجمة قبل التنفيذ
- له خطوة بناء منفصلة تشغّلها بنفسك
- المخرَج ملف تنفيذي يبقى على القرص
- تُكتشَف أخطاء كثيرة قبل أول تشغيل
- تغيير الملف بلا أثر حتى تعيد البناء
ترجمة أثناء التنفيذ
- لا خطوة منفصلة؛ تسلّم الملف فيُنفَّذ
- قد تُحفَظ نتيجة الترجمة في الذاكرة أو على القرص
- لا تُكتشَف أخطاء كثيرة حتى الوصول إلى ذلك السطر
- تغيّر الملف فترى الأثر في المرة التالية
لم يعد هذان العمودان منفصلين تماما: فأغلب المحركات الحديثة تجمع بين الأمرين، وهذا ما جعل التسمية بلا فائدة.
أين تقع بايثون وجافاسكريبت وPHP من هذه السلسلة؟
يضع الجدول التالي سؤالي القسم السابق أمام أربع لغات. والعمود الثالث هو المهم، لأن اختلاف اللغات الحقيقي هناك لا في تسميتها.
| اللغة | ما الذي يترجمها | ماذا تنتج الترجمة | أين تُنفَّذ |
|---|---|---|---|
| بايثون | مفسر بايثون | شفرة بايت؛ وللوحدات تُحفَظ على القرص في مجلد __pycache__ | الجهاز نفسه أو الخادم |
| جافاسكريبت | محرك المتصفح، أو Node على الخادم | شفرة آلة تُبنى أثناء التشغيل ولا تُحفَظ على القرص | متصفح المستخدم، أو خادم |
| PHP | محرك PHP | شفرة تشغيلية؛ يحفظها opcache في ذاكرة الخادم لا على القرص | الخادم |
| سي | مترجِم، في خطوة منفصلة قبل التشغيل | ملف تنفيذي على القرص | مباشرة على نظام التشغيل |
ضع الصفوف الثلاثة الأولى جنبا إلى جنب فيظهر نمط: ثلاثتها تترجم وقت التشغيل، ولا تختلف إلا في مكان وضع النتيجة. بايثون على القرص، وPHP في الذاكرة، وجافاسكريبت في لا مكان. والصف الرابع موجود عمدا لترى الحالة المختلفة فعلا: في سي، إن لم تترجم البرنامج فلا يوجد شيء لتنفيذه أصلا.
ونتيجة عملية تنفعك فورا: في جافاسكريبت وPHP وبايثون تغيّر الملف فترى الأثر في المرة التالية. أما في سي فعليك البناء من جديد. وإن قال لك أحد إنه غيّر الملف ولم يتغير شيء، فالسؤال الأول أي صف من هذا الجدول تقع لغته فيه، والسؤال الثاني هو موضوع القسم الخامس.
لماذا لا يُنفَّذ أي سطر أحيانا وينفَّذ نصف البرنامج أحيانا؟
أنشئ ملفين صغيرين وشغّل كلا منهما وحده. الأول bad.py، وفيه قوس غير مغلق في السطر الثاني:
print("first line")
print("second line"والمخرج الحقيقي هو:
File "bad.py", line 2
print("second line"
^
SyntaxError: '(' was never closedوالثاني boom.py، ونحوه سليم لكنه يطلب شيئا غير ممكن:
print("first line")
print(1/0)ومخرجه:
first line
Traceback (most recent call last):
File "boom.py", line 2, in <module>
print(1/0)
~^~
ZeroDivisionError: division by zeroانظر إلى الفرق، فهذا الأمر وحده نصف عملية تصحيحك للأخطاء. في الملف الأول لم تُطبَع عبارة first line أبدا، مع أن السطر الأول كان سليما تماما وسبق موضع الخطأ. وفي الملف الثاني طُبعت ثم سقط البرنامج في منتصف الطريق. والسبب هو سلسلة القسم الأول: فحص النحو يجري على الملف كله قبل أي تنفيذ، فقوس غير مغلق في السطر الثاني يُسقط معه السطر الأول. أما القسمة على صفر فلا تُكتشف حتى اللحظة التي نصل فيها إليها فعلا، لأن أحدا قبل ذلك لا يعرف أن المقام صفر.
ولهاتين الحالتين اسمان: خطأ نحوي وخطأ زمن التنفيذ. وفائدتهما العملية أنك تميز بينهما من المخرج وحده، حتى لو لم تكن تعرف اللغة. فإن طُبع شيء قبل رسالة الخطأ فقد بدأ البرنامج بالعمل وأنت أمام خطأ زمن تنفيذ؛ وإن لم يُطبع شيء فالأرجح أنه لم يُنفَّذ أصلا.
سؤال واحد يحدد نوع الخطأ
قبل رسالة الخطأ، هل طبع البرنامج شيئا؟
خطأ نحوي
- لم يبدأ البرنامج أصلا؛ انكسر فحص النحو قبل التنفيذ
- رقم السطر في الرسالة صحيح عادة، أو بعده بسطر أو سطرين
- ابحث عن قوس أو علامة اقتباس أو نقطتين ناقصة
خطأ زمن التنفيذ
- عمل البرنامج ثم اصطدم في منتصف الطريق بما ليس ممكنا
- آخر سطر في الرسالة يذكر نوع الخطأ وهو الأهم
- اطبع القيم هناك مباشرة لترى أيها ليس كما افترضت
يخبرك السؤال بنوع الخطأ لا بسببه. والبرنامج الذي يعطي جوابا خاطئا دون أي خطأ لا يقع في أي من الفرعين.
الملف الذي حفظته ليس ما يُنفَّذ
والآن الجزء الطريف من السلسلة. نتيجة الترجمة لا تعيش في الذاكرة فحسب؛ بل تُحفَظ في أحيان كثيرة كي لا يلزم بناؤها من جديد في المرة التالية. وبايثون تفعل ذلك أمام عينيك مباشرة. أنشئ ملفين، tools.py وmain.py يستورد الأول، ثم:
python3 main.py
ls __pycache__/يظهر مجلد اسمه __pycache__ وفيه ملف واحد بالضبط: tools.cpython-312.pyc. ولم يُكتب شيء من أجل main.py. وتوثيق بايثون نفسه يشرح الشقين: بايثون تحفظ النسخة المترجَمة من كل وحدة في مجلد __pycache__، أما الوحدة التي تُحمَّل مباشرة من سطر الأوامر فتعيد ترجمتها دائما ولا تحفظ النتيجة.
وPHP تؤدي العمل نفسه بفارق واحد: لا تضع النتيجة على القرص بل تحفظها في ذاكرة الخادم. واسم هذه الطبقة opcache، ولسلوكها إعداد واحد يصيب المبتدئين بالجنون: كل كم يفحص هل تغيّر الملف على القرص. ودليل PHP نفسه يصف الإعداد بهذه العبارة تحديدا، كم مرة تُفحَص أزمنة تعديل الملفات، بالثواني. أي أن بين حفظ الملف وسريان أثره فجوة، وهذه الفجوة مقصودة.
لذلك فالجملة المألوفة «غيّرت الملف ولم يحدث شيء» لها ثلاثة أجوبة شائعة، وثلاثتها داخل هذه السلسلة: إما أن الملف الذي غيّرته ليس الملف الذي يُنفَّذ، وإما أن نسخة مترجَمة قديمة ما زالت في الذاكرة، وإما أن المخرَج يأتي من طبقة كاش أمامك ولم يصل برنامجك أصلا. وقسم «من عملنا نحن» أسفل هذه الصفحة يعطي رقم ذلك الإعداد على خادمنا نحن.
الطبقات القابعة بين نصك وبين المعدن
-
1
المعالج
الشيء الوحيد الذي ينفذ فعلا، ولا يفهم إلا تعليمات رقمية بسيطة.
-
2
نظام التشغيل
يقسم الذاكرة والملفات ووقت المعالج بين البرامج.
-
3
محرك اللغة
مفسر بايثون، أو محرك جافاسكريبت في المتصفح، أو محرك PHP على الخادم.
-
4
الصيغة المترجَمة
شفرة بايت أو تشغيلية أو شفرة آلة؛ وهي ما يُقرأ ويُنفَّذ فعلا.
-
5
ملفك النصي
الطبقة الأبعد، والوحيدة التي تغيّرها مباشرة.
الأرقام ترتيب مسافة لا ترتيب أهمية. والبرمجة اليومية لا تحتاج إتقان أي من هذه الطبقات؛ تحتاج فقط أن تعرف بوجودها.
حين لا يعمل البرنامج، أين تنظر أولا؟
اقرأ رسالة الخطأ من الأسفل لا من الأعلى. فآخر سطر يذكر نوع الخطأ وهو أهم كلمة على الشاشة؛ والسطر الذي فوقه يقول عادة أين. وفي مثال القسم الرابع كان آخر سطر ZeroDivisionError: division by zero، والسطر فوقه يشير إلى التعبير الذي انفجر بالضبط. أما المبتدئون فيبدؤون عادة من الأعلى، حيث لا تُكتب إلا مسارات الملفات، ثم يقولون إن الخطأ غير مفهوم.
وبعد القراءة، سمِّ الطبقة. وهي أربع لا أكثر. النحو: لم يُنفَّذ شيء، وجاءت الرسالة قبل أي مخرَج. زمن التنفيذ: طُبع شيء ثم سقط البرنامج. المحيط: البرنامج سليم لكن شيئا يحتاجه غير مثبَّت، أو نسخته مختلفة، أو لا يُعثر على مساره. المنطق: لا خطأ أصلا، يعمل البرنامج حتى نهايته ويعطي جوابا خاطئا.
وموقفنا هنا بسيط: قبل أن تغيّر حرفا واحدا، قل اسم الطبقة بصوت مسموع. فأغلب الساعات التي يهدرها المبتدئون تذهب في تعديل شفرة داخل طبقة ليست فيها المشكلة، كإعادة كتابة دالة سليمة بينما المكتبة لم تكن مثبَّتة أصلا. والطبقة الرابعة أسوأها، لأنها لا تعطي أي خطأ؛ والسبيل الوحيد لاصطيادها أن تخمّن المخرَج المتوقع قبل التشغيل ثم تقارنه بما خرج فعلا.
المسار السريع مع الذكاء الاصطناعي
ما جعله النموذج اللغوي أسرع فعلا للمبتدئ هو قراءة رسالة الخطأ، لكن بشرط الالتزام بالترتيب. والخطأ الشائع أن يخمّن المرء موضع المشكلة ثم يسأل عن تخمينه؛ فيأتيه جواب عن تخمينه لا عن الخطأ. والوصفة أدناه تعكس الترتيب: الطبقة أولا، ثم الشرح، ثم التغيير. ونموذج من الطبقة المتقدمة أنسب لهذا لأن العمل تشخيص؛ واختيارنا الحالي في قسم الذكاء الاصطناعي في هذا الموقع.
- انسخ نص الخطأ كاملا من أول سطر إلى آخره. لا لقطة شاشة ولا ملخصا ولا السطر الأخير وحده؛ فالأسطر العليا تحمل موضع الوقوع.
- قبل أي شرح، اطلب تسمية الطبقة فقط: نحو، أو زمن تنفيذ، أو محيط، أو منطق. كلمة واحدة تكفي، وهي التي تحدد ما بعدها.
- اطلب أن يقتبس حرفيا سطر شفرتك الذي يشير إليه الخطأ. فإن عجز، فهو لا يملك شفرتك بعد وكل ما يقوله تخمين.
- ثم اطلب الشرح وأصغر تغيير ممكن، لا إعادة كتابة الملف. فإعادة الكتابة الكاملة تخفي الخلل ولن تعرف أبدا ما كان.
- طبّق التغيير بنفسك وشغّله، وإن أعطى خطأ من جديد فابدأ من الخطوة الأولى. وإن كانت الطبقة هي المحيط، فاذكر في رسالتك التالية نسخة اللغة ونظام التشغيل.
وصفة جاهزة للنسخ
واجهت هذا الخطأ. قبل أي شرح، أجب فقط عن الأسئلة الثلاثة التالية وبهذا الترتيب.
١. في أي طبقة يقع هذا الخطأ؟ اختر واحدة فقط: نحو، زمن تنفيذ، محيط، منطق.
٢. أي سطر من شفرتي تسبب فيه؟ اقتبس ذلك السطر حرفيا. وإن لم تكن تملك الشفرة فقل ما الذي تحتاجه.
٣. ما أصغر تغيير يصلح هذا؟ ذلك التغيير وحده، دون إعادة كتابة بقية الملف.
نص الخطأ كاملا:
{الصق هنا، من أول سطر إلى آخره}
الشفرة التي شغّلتها:
{الصق هنا}
المحيط: {نسخة اللغة} على {نظام التشغيل}
قبل أن تثق بالناتج: طبقة المحيط هي أسوأ ما يخمّنه النموذج، لأنه لا يرى حاسوبك: لا يعرف نسخة لغتك ولا نظام تشغيلك ولا ما ثبّتّه، وإن لم تزوّده بها ملأ الفراغ بتخمين. لذا تحقق من أي مسار ملف أو اسم أمر يقترحه بفحص بسيط قبل تشغيله. وإن خالف جواب الخطوة الأولى ما رأيته أنت في المخرَج، فثق بالمخرَج لا بالجواب.
الذكاء الاصطناعي في هذا العمل
عادة واحدة ترفع سرعة المبتدئ أكثر من أي شيء آخر: بدل البحث عن نص الخطأ في المنتديات، سلّم النص كاملا إلى نموذج واطلب منه شرحه. فما كان يستغرق ساعة من التنقيب صار رسالة واحدة. وموقفنا أن لهذا العمل ترتيبا أيضا: الطبقة أولا ثم الشرح؛ والعكس إهدار وقت يرتدي زي الإنتاجية.
أدوات تساعد فعلا
- Claude جيد في قراءة رسالة خطأ طويلة وتحديد أي أسطرها مهم، خصوصا حين تسلّم النص كاملا دون تلخيص. وإيران ليست في أي من قائمتي الدول المدعومة لدى أنثروبيك؛ قرأنا ذلك في صفحة أنثروبيك نفسها لا عبر اختبار شبكة.
- Gemini يتعامل مع الفارسية جيدا ويكفي لسؤاله عن معاني مصطلحات مثل شفرة البايت والشفرة التشغيلية والترجمة في الوقت المناسب. وصفحة غوغل نفسها تقول إن تطبيق جيميناي على الويب يعمل في أكثر من ٢٣٠ دولة وإقليما، وإيران ليست في تلك القائمة.
- ChatGPT الخيار الأشيع ومقبول لقراءة الأخطاء. وليست لدينا صفحة له في قسم الذكاء الاصطناعي بعد، فلا ندّعي هنا شيئا عن إتاحته أو سعره.
اين ينقلب ضدك
الخطر هنا خاص بهذا الدرس تحديدا: يجيب النموذج عند الطبقة التي أشار إليها سؤالك، وسؤال المبتدئ يسمي الطبقة الخطأ عادة. اسأل لماذا لا تعمل حلقتك بينما المشكلة الحقيقية مكتبة غير مثبّتة، فتحصل على جواب مفصل عن الحلقات لا علاقة له بالسبب وقد يكلفك ساعة. والعلاج هو المسار السريع في هذه الصفحة: نص الخطأ كاملا أولا، ثم الطبقة، ثم الشرح. وطبقة المحيط أخطرها جميعا لأن النموذج لا يرى حاسوبك، وإن لم تعطه النسخ ملأ الفراغ بتخمين؛ وأنثروبيك نفسها تسمي هذا هلوسة في وثائقها وتشرح كيفية تقليله، أي أن الجهة الصانعة تعامله ضعفا حقيقيا. والقاعدة بسيطة: كل ما يقوله النموذج عن نظامك يجب أن يُقاس بتشغيل حقيقي لا بثقة نبرته. ولمعرفة كيف يمكن الدفع لكل من هذه الأدوات من إيران، انظر دليل الشراء.
المصادر: Anthropic: reduce hallucinations Anthropic: supported countries Google: where the Gemini web app is available
حدود هذه النصيحة
فهم هذه السلسلة لا يصلح شيئا؛ بل يجعلك تنظر في المكان الصحيح حين تصلح. ونموذج هذه الصفحة مبسّط عن قصد أيضا: فالترجمة في الوقت المناسب، وجمع المهملات، والتحسينات التي يجريها محرك اللغة أثناء التنفيذ، وجدولة نظام التشغيل، كل منها حقل قائم بذاته ولم يرد هنا إلا اسمه. والتقسيم الرباعي للطبقات أداة عمل لا تصنيفا رسميا؛ فعمليا قد تكون جذور خلل واحد في طبقتين معا. ورقم opcache في القسم التالي يخص خادمنا نحن: فقد يكون صفرا على استضافتك، أي يفحص عند كل طلب، أو قد يكون معطلا بالكامل.
من عملنا نحن
يوم مراجعة هذا الدرس، قسنا شقّي القسم الخامس على هذا الخادم نفسه. بايثون أولا: شُغّل main.py يستورد tools.py، وبعدها احتوى مجلد __pycache__ ملفا واحدا بالضبط، هو tools.cpython-312.pyc، ولم يُكتب شيء من أجل main.py نفسه. ثم PHP: قيمة الإعداد opcache.revalidate_freq على هذا الخادم هي ٦٠. غيّرنا ملف بيانات في قسم التعلم هذا بعد ثانية واحدة من طلب، ثم جلبنا الصفحة كل خمس ثوان؛ فظلت القيمة القديمة تعود حتى الثانية ٤٣، وظهرت الجديدة في الثانية ٤٨. أي أن كل زائر كان يُخدَم قرابة دقيقة بنسخة سبق أن ترجمها PHP، بينما الملف على القرص قد تغيّر منذ الثانية الأولى. وإن كان لك وصول إلى خادمك، فانظر الشيء نفسه بـ php -i | grep opcache.revalidate_freq.
اسئلة متابعة حقيقية
ماذا تعني شفرة البايت؟
إنها صيغة وسيطة: ليست نصا يقرأه الإنسان ولا تعليمة مباشرة للمعالج. يحوّل مفسر اللغة شفرتك إلى شفرة بايت ثم ينفذها بنفسه، ولأن التحويل يحدث مرة واحدة فإن حفظه يجعل التشغيل التالي أسرع. والملفات داخل مجلد __pycache__ هي هذا بالضبط.
لماذا تكون بعض اللغات أسرع من غيرها؟
لأنها تترك عملا أقل لوقت التنفيذ. فاللغة التي تُترجَم قبل التشغيل تكون قد اتخذت أغلب القرارات مسبقا، أما التي تترجم أثناء التنفيذ وتستنتج أنواع البيانات في اللحظة نفسها فعليها القيام بالعمل ذاته في منتصف التشغيل. لكن اللغة ليست عنق الزجاجة في أغلب المواقع أصلا؛ فقاعدة البيانات والشبكة والصور الثقيلة تظهر قبلها بكثير.
غيّرت الملف ولم يتغير الموقع، أين المشكلة؟
له ثلاثة أجوبة شائعة، وثلاثتها داخل هذه السلسلة. إما أن الملف الذي غيّرته ليس الملف الذي يُنفَّذ، كأن تُحمَّل نسخة أخرى من مجلد آخر. وإما أن نسخة مترجَمة قديمة ما زالت في ذاكرة الخادم ولا تتغير حتى تمضي فترة الفحص. وإما أن المخرَج يأتي من طبقة كاش أمامك ولم يصل برنامجك أصلا.