البرمجة

الشروط والحلقات في البرمجة

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

  • الدرس 6 من 14
  • مبتدئ
  • مجاني، دون تسجيل

دورة كاملة من الحلقة، وطريق الخروج الوحيد

شرط التوقفيُختبر من جديد في أول كل دورة
  • 1 اختبر الشرط
  • 2 نفّذ الجسم
  • 3 غيّر شيئا واحدا
  • 4 عُد إلى الشرط

الطريق الوحيد للخروج من هذه الدائرة أن يصير الشرط الذي في أعلاها خاطئا. والحلقة التي لا يغير جسمها شيئا مما يقرأه الشرط لا تخرج أبدا.

آخر مراجعة: تُراجع الحقائق وأسماء الأدوات في هذا الدرس مقابل مصادرها في هذا التاريخ.

ما الشرط، وما الذي ينقص البرنامج بدونه؟

الشرط سؤال له جوابان فقط، نعم أو لا، ويسلك البرنامج أحد طريقين بحسب الجواب. والبرنامج الذي لا شرط فيه قائمة تعليمات تُنفَّذ دائما بالطريقة نفسها من أولها إلى آخرها؛ ومع أول شرط تتحول تلك القائمة إلى شيء يتفاعل مع الحال.

age = 17

if age >= 18:
    print("allowed")
else:
    print("denied")

السطر الثاني هو السؤال، والثالث طريق نعم، والخامس طريق لا. وelse اختياري: إن تركته لم يحدث شيء حين يكون الجواب لا، ويمر البرنامج بجانب الشرط دون كلمة. وهذا الصمت يهم لاحقا.

خطآن كلاسيكيان يتربصان هنا. الأول الخلط بين علامة مساواة واحدة واثنتين: = تعني أسنِد، و== تعني قارِن. وبايثون رحيمة في هذا وترفع خطأ نحويا إن وضعت علامة واحدة داخل شرط، أما اللغتان اللتان بُني بهما هذا الموقع، PHP وجافاسكريبت، فلا ترفعان شيئا: if ($x = 5) تُسنِد ثم تكون صحيحة دائما، فيصير شرطك غير موجود عمليا ولا يقول أحد شيئا.

والخطأ الثاني هو الترتيب، وقلما يُتحدث عنه. حين تصف عدة شروط متتالية، يفوز أول شرط يتحقق ولا تُقيَّم البقية أصلا. فإن وضعت الشرط العام فوق الشرط الخاص، لم يُنفَّذ الخاص أبدا وقضيت ساعات تطارد خللا غير موجود. الشفرة صحيحة؛ ترتيبها وحده هو الخطأ.

سؤال واحد، وطريقان، وبقية الشفرة التي لا تُقيَّم

هل الشرط قائم؟

نعم

يُنفَّذ جسم if

  • لا تُقيَّم الفروع الأخرى إطلاقا
  • يتابع البرنامج بعد الجسم
لا

يُنفَّذ جسم else إن كتبته

  • بدون else لا يحدث شيء ولا يُبلَّغ عن شيء
  • وهذا الصمت أصعب الحالات اكتشافا

حين تتوالى عدة شروط، يفوز أول شرط صحيح ولا تُقيَّم البقية إطلاقا. فلا تضع الشرط العام فوق الخاص.

ما الحلقة، وما الذي تحتاجه كل حلقة؟

الحلقة تعني: نفّذ هذا العمل أكثر من مرة بدل مرة واحدة. وقيمتها في العدد. فكتابة ثلاثة أسطر لثلاثة ملفات ليست إنجازا، لكن الأسطر الثلاثة نفسها لثلاثين ألف ملف لا تمكن إلا بحلقة، والأهم أن شفرتك تصير مستقلة عن العدد.

هناك شكلان رئيسيان. الأول يمر على مجموعة وينفَّذ مرة لكل عضو:

names = ["ali", "sara", "reza"]

for name in names:
    print(name)

والثاني يدور ما دام شرط قائما، وعدد المرات غير معروف مسبقا:

size = 4096
step = 0
units = ["B", "KB", "MB", "GB"]

while size >= 1024 and step < len(units) - 1:
    size = size / 1024
    step = step + 1

كل حلقة، أيا كان شكلها، تحتاج ثلاثة أشياء وتنكسر إن غاب واحد منها: شيء تمر عليه أو شرط تختبره، وشيء يتغير في كل دورة، وطريق للانتهاء. وفي المثال الثاني size وstep هما ما يتغير؛ احذف سطر size = size / 1024 يبق الشرط صحيحا إلى الأبد.

وتفصيل تتخطاه الدروس عادة: النصف الثاني من ذلك الشرط، أي step < len(units) - 1، لا علاقة له بالحجم. كل ما يفعله أن يمنع الحلقة من تجاوز آخر قائمة الوحدات. وهذه الحلقة موجودة في شفرة لوحة هذا الموقع نفسه، وذلك النصف الثاني هو الشيء الوحيد الواقف بينها وبين خطأ حقيقي.

أي حلقة تُستعمل أين؟ عدّ حقيقي من شفرة هذا الموقع

أغلب الدروس تبدأ بحلقة العد، تلك التي يصعد فيها عداد من الصفر إلى رقم ما. وهي في الشفرة الحقيقية أقل الأشكال استعمالا، وبدل التخمين عددنا قالب هذا الموقع.

في ١٩٣ ملف PHP في القالب توجد ٥٦٠ حلقة على مجموعة، و٢١ حلقة عد، و١٣ حلقة شرطية. أي أن الشكل الذي تتعلمه أولا يمثل أقل من أربعة في المئة من حلقات هذه الشفرة.

والسبب واضح: أنت لا تريد رقم العضو تقريبا أبدا، بل تريد العضو نفسه. فحين تكتب «لكل ملف في هذا المجلد» لا يفيدك رقم الملف، والاحتفاظ بعداد ليس إلا موضعا إضافيا للخطأ. والواحد والعشرون الباقية هي بالضبط المواضع التي يلزم فيها الرقم فعلا: رسم خمس نجوم تقييم، وبناء أعمدة جدول، والرجوع إلى الوراء عبر شهور رسم بياني.

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

والقاعدة الخارجة من هذا العدّ أنفع من أي تعريف: إن كنت تعرف على أي شيء تمر، فاكتب حلقة مجموعة. وإن كنت لا تعرف متى تنتهي، فحلقة شرطية. وإن كنت تحتاج الرقم نفسه فعلا، فحلقة عد، وهذا نادر عمليا.

حلقة على مجموعة أم حلقة شرطية؟

حلقة على مجموعة

  • حين تعرف مسبقا على أي شيء تمر
  • المجموعة نفسها تحدد عدد الدورات
  • الحلقة اللانهائية شبه مستحيلة
  • تُنفَّذ صفر مرة على مجموعة فارغة
  • في قالب هذا الموقع: ٥٦٠ منها

حلقة شرطية

  • حين لا تعرف كم دورة يستغرق
  • عليك أنت أن تغير شيئا كي تنتهي
  • القراءة حتى نهاية رد، أو صنع اسم فريد
  • كل حلقة لانهائية تأتي من هذه العائلة
  • في قالب هذا الموقع: ١٣ منها

لا أحد منهما أفضل من الآخر؛ إنهما لحالتين مختلفتين. وعدّنا على هذه الشفرة يبين أن العمود الأول يلزم أكثر بكثير عمليا، لا أن الثاني خطأ.

كيف تبدو الحلقة اللانهائية على خادم حقيقي؟

الحلقة اللانهائية حلقة لا يصير شرط توقفها خاطئا أبدا. ولها شكلان شائعان يشتركان في جذر واحد: ما يقرأه الشرط لا يتغير داخل جسم الحلقة.

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

والآن السؤال الذي لا يجيبه أي درس: ماذا يحدث فعلا إن وقع هذا على موقع حي؟ المتصفح لا ينتظر إلى الأبد. على هذا الخادم إعداد اسمه request_terminate_timeout وقيمته ١٨٠ ثانية. أي أن كل طلب لم ينته بعد ثلاث دقائق تُقتل عمليته.

وكلمة «تُقتل» هنا دقيقة لا مجازية. فهذا ليس خطأ PHP يمكنك التقاطه؛ العملية تُغلق من الخارج. والنتيجة أن شفرتك الختامية لا تُنفَّذ: فإن كانت الحلقة في منتصف كتابة ملف بقي الملف نصف مكتوب، وإن كانت قد أخذت قفلا لم يُحرَّر القفل. أما الزائر فيرى صفحة خطأ خادم عامة لا تقول شيئا عن حلقتك.

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

الشروط داخل الحلقات: break وcontinue وخطأ الواحد

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

كلمتان تغيران العمل هنا. break تترك الحلقة كلها وتخرج؛ وcontinue تترك هذه الدورة وحدها وتنتقل إلى العضو التالي. الأولى لحين تجد جوابك، والثانية لحين لا يفيدك هذا العضو.

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

وخطأ آخر له اسم خاص، خطأ الواحد: تُنفَّذ الحلقة مرة أكثر أو مرة أقل مما توقعت. ومصدره الأساسي أن المدى مغلق من أوله ومفتوح من آخره عادة. ففي بايثون تعطيك range(1, 10) الأرقام من واحد إلى تسعة ولا تعطي العشرة. وهذا القرار ليس غريبا وله سببه، لكن الحفظ لا ينفع؛ الذي ينفع أن تكتب مرة واحدة وتعد المخرَج.

والحالة الأخيرة، وهي أهدأ الحالات: حلقة على مجموعة فارغة. تُنفَّذ صفر مرة، ولا ترفع خطأ، ولا تطبع شيئا. والبرنامج الذي لم يعمل شيئا ولم يشتكِ من شيء هو دائما أول ما تفحصه لا آخره.

كيف تقرأ حلقة لم تكتبها أنت؟

هذه المهارة أهم اليوم من كتابة الحلقات، لأن الشفرة تُقرأ أكثر بكثير مما تُكتب، ولأن جزءا كبيرا من تلك الشفرة لم تكتبه أنت. وطريقتنا أربعة أسئلة، وترتيبها مهم.

واحد: على أي شيء تمر، وهل يمكن أن يكون ذلك الشيء فارغا؟ إن أمكن، نُفِّذت الحلقة صفر مرة، وهذه عادة الحالة التي لم يختبرها أحد.

اثنان: ما الذي يتغير في كل دورة؟ إن كان جوابك «لا شيء»، فإما أن الحلقة لانهائية وإما أنها زائدة.

ثلاثة: ما الذي يوقفها، وهل يمكن ألا يحدث ذلك أبدا؟ وابحث هنا عن جمل break أيضا، فالخروج المبكر لا يُكتب دائما في أعلى الشفرة.

أربعة: ما الصحيح بعد الدورة الأخيرة؟ ما القيم التي تحملها المتغيرات المبنية داخل الحلقة بعدها، وهل يعتمد عليها أحد؟

جرّب هذه الأربعة على حلقة تحويل الوحدات أعلاه: لا تمر على مجموعة فلا معنى للفراغ، ويتغير شيئان، وللشرط نصفان أحدهما حارس فحسب، وبعد الدورة الأخيرة يحمل step رقم الوحدة الصحيحة وهو بالضبط ما يستعمله السطر التالي. أربعة أجوبة، وقد فهمت الحلقة الآن دون أن تشغّلها.

المسار السريع مع الذكاء الاصطناعي

ما تغير فعلا في هذا الموضوع ليس كتابة الحلقات؛ فكتابتها لم تكن يوما الجزء الصعب. الذي تغير أن عشر حلقات في اليوم تصلك الآن ولم تكتبها أنت، وأمامك ثلاثون ثانية لتعرف أين تنكسر كل واحدة. لذلك لا تطلب الوصفة أدناه شفرة من النموذج؛ بل تطلب منه أن يجيب عن الأسئلة الأربعة أعلاه بخصوص شفرة تملكها. ونموذج من الطبقة المتقدمة أنسب لهذا من نموذج سريع ورخيص، لأن العمل عمل حكم؛ واختيارنا الحالي بين نماذج البرمجة موجود في قسم الذكاء الاصطناعي في هذا الموقع.

  1. انسخ الحلقة كاملة مع بضعة أسطر فوقها وتحتها، لا الحلقة وحدها. فالقيمة التي يقرأها الشرط تُبنى عادة خارج الحلقة، وبدونها يكون جواب النموذج تخمينا.
  2. أرسل الوصفة أدناه وقل صراحة ألا يعيد كتابة الشفرة. فإن سمحت بإعادة الكتابة حصلت على نسخة جديدة بدل شرح، وأول ما يضيع هو الحالة نفسها التي أردت فهمها.
  3. خذ جواب الحالة الفارغة على محمل الجد. ففي تجربتنا هذا أكثر ما تكشفه هذه الوصفة: حلقة تدور صفر مرة على قائمة فارغة، ولا شيء في الشفرة بعدها يحسب حساب ذلك.
  4. قبل قبول الجواب، شغّل الحلقة بنفسك بمدخل فارغ وبمدخل من عضو واحد. هاتان الحالتان أرخص اختبار موجود، وأغلب أخطاء الحلقات تظهر هناك.

وصفة جاهزة للنسخ

هذه الشفرة ليست لي وأريد فهمها لا تغييرها.

{ضع الشفرة هنا، مع بضعة أسطر فوق الحلقة وتحتها}

لا تعد كتابة الشفرة. أجب فقط عن هذه الأسئلة الأربعة، جملتين لكل سؤال:

١. على أي شيء تمر هذه الحلقة، وهل يمكن أن يكون فارغا؟ وماذا يحدث إن كان كذلك؟
٢. ما المتغيرات التي تتغير في كل دورة بالضبط؟
٣. ما الذي يوقف الحلقة، وهل هناك حالة لا تتوقف فيها أبدا؟ واذكر أي خروج مبكر.
٤. بعد الدورة الأخيرة، ما القيمة التي يحملها كل متغير بُني داخل الحلقة؟

وفي النهاية، قل أيا من هذه الأجوبة الأربعة لا تستطيع التأكد منه من الشفرة وحدها، وما الذي تحتاجه لتعرف.

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

الذكاء الاصطناعي في هذا العمل

الحلقات والشروط هي بالضبط نوع الشفرة الذي تكتبه النماذج اللغوية بلا عيب. فآلاف الأمثلة على هذه البنى نفسها كانت في بيانات تدريبها، والنتيجة أن إنتاجها لم يعد يُحسب مهارة. فالذي يبقى لك هو القراءة: أن تعرف أين تنكسر هذه الحلقة، وأي حالة نسيها هذا الشرط، وهل تفعل هذه الشفرة ما تظنه على مدخل فارغ. وموقفنا أن تُبقي النموذج في دور الشارح لا الكاتب في هذا الموضوع؛ فالشفرة التي لم تكتبها ولا تستطيع شرحها سطرا سطرا ليست شفرتك.

أدوات تساعد فعلا

  • Claude جيد في أخذ مقطع شفرة ووصف سلوكه، وحين تقول صراحة لا تعد كتابته يقل ارتجاله لنسخة جديدة. وإيران ليست في أي من قائمتي الدول المدعومة لدى أنثروبيك؛ قرأنا ذلك في صفحة أنثروبيك نفسها لا عبر اختبار شبكة.
  • Claude Code أداة سطر أوامر، وميزتها في هذا العمل شيء واحد: بدل مقطع شفرة ترى المستودع نفسه، فتستطيع أن تذهب لترى من أين جاءت المجموعة التي تمر عليها حلقتك. تُثبَّت مجانا لكنها لا تعمل دون اشتراك كلود أو حساب في كونسول أنثروبيك، وإيران ليست في قائمة الدول المدعومة.
  • Gemini له وضع دراسة منفصل اسمه Guided Learning يطرح أسئلة بدل تسليم الأجوبة، وهو مناسب لأسئلة قراءة الحلقة الأربعة. وصفحة غوغل نفسها تقول إن تطبيق جيميناي على الويب يعمل في أكثر من ٢٣٠ دولة وإقليما، وإيران ليست في تلك القائمة.

اين ينقلب ضدك

الخطر المحدد في هذا الموضوع هو: يتحدث النموذج عن حلقة بثقة كاملة وهو لم يرَ أصلا المجموعة التي تمر عليها. فإن كانت تلك المجموعة آتية من استعلام قاعدة بيانات أو طلب شبكة ونسخت أنت الحلقة وحدها، فإن جملة «هذه الحلقة آمنة» تخمين كُتب ليبدو حقيقة. وأنثروبيك نفسها تسمي هذا هلوسة في وثائقها وتشرح طرق تقليله، أي أن الجهة الصانعة تعامله ضعفا حقيقيا لا إشاعة.
والخطر الثاني أكثر عملية: حين تطلب من النموذج أن يجعل حلقة بطيئة أسرع، تتصرف النسخة التي يعيدها غالبا تصرفا مختلفا عند الحواف، خصوصا على مدخل فارغ وعلى مدخل من عضو واحد. فاختبر كل إعادة كتابة بهذين المدخلين، لا بالمدخل العادي الذي ينجح دائما. ولمعرفة كيف يمكن الدفع لكل من هذه الأدوات من إيران، انظر دليل الشراء، ولاختيارنا الحالي بين نماذج البرمجة انظر أفضل ذكاء اصطناعي للبرمجة.

المصادر: Anthropic: reduce hallucinations Anthropic: supported countries Claude Code: overview and install Google: where the Gemini web app is available

حدود هذه النصيحة

نحو هذه الصفحة بايثون. وشكل الشرط والحلقة موجود في كل لغة، لكن الكتابة تختلف، ونسخ هذه الأسطر مباشرة إلى PHP أو جافاسكريبت لن ينجح. وثانيا، يترك هذا الدرس عمدا الاستدعاء الذاتي والمولدات والاشتقاق القائمي؛ فهي طرق أخرى للعمل نفسه ولا تبدأ في اكتساب معنى إلا بعد رسوخ هاتين البنيتين الأساسيتين. وثالثا، ما كنت تنتظره على الأرجح: أي حلقة أسرع. نحن لا نعطي أي رقم عن السرعة، لأن الفرق عند الأحجام التي يعمل بها المبتدئ غير قابل للقياس، ولأننا لم نقسه على هذا الخادم أيضا. والحلقة الصحيحة المقروءة أفضل من حلقة قد تكون أسرع.

من عملنا نحن

يوم مراجعة هذا الدرس، وبدل التخمين، فتحنا ملف إعدادات PHP الخاص بهذا الخادم. ففي /www/server/php/85/etc/php-fpm.conf سطر يقول request_terminate_timeout = 180. أي أن كل طلب لم ينته بعد ١٨٠ ثانية تُغلق عمليته من الخارج. ودليل PHP الرسمي يقول الشيء نفسه: هذا الإعداد مهلة تُقتل بعدها عملية العامل. والنتيجة المهمة لهذا الدرس، والتي لا يكتبها أي درس عن الحلقات، هي أنه بما أن العملية تُقتل ولا يقع خطأ، فإن شفرتك الختامية لا تُنفَّذ. والحلقة التي كانت في منتصف كتابة ملف تترك ذلك الملف نصف مكتوب. وأجرينا العدّ نفسه على قالب الموقع فوصلنا، في ١٩٣ ملف PHP، إلى ٥٦٠ حلقة على مجموعة، و٢١ حلقة عد، و١٣ حلقة شرطية؛ والأرقام الثلاثة كلها يمكن إعادة إنتاجها ببحث بسيط واحد في ذلك المجلد.

اسئلة متابعة حقيقية

ما الفرق بين for وwhile عمليا؟

الفرق عمليا هو من يحدد عدد الدورات. ففي الحلقة على مجموعة تحدد المجموعة ولا تفعل أنت شيئا؛ وفي الحلقة الشرطية عليك أنت أن تغير شيئا وإلا لم تنته أبدا. ولهذا أيضا تكون كل الحلقات اللانهائية تقريبا من النوع الثاني.

متى أستعمل break وcontinue؟

حين تجد جوابك ولا فائدة من متابعة الحلقة، استعمل break. وحين لا يفيدك هذا العضو بعينه وقد يفيدك غيره، استعمل continue. وتحذير واحد: في الحلقات المتداخلة، تكسر break الداخلية وحدها ويبدأ البرنامج بهدوء الدورة التالية من الخارجية.

لماذا تُنفَّذ حلقتي مرة أكثر أو أقل مما توقعت؟

اسم هذا خطأ الواحد، ومصدره الأساسي أن المدى مغلق من أوله ومفتوح من آخره عادة: ففي بايثون تعطيك range(1, 10) الأرقام من واحد إلى تسعة. والحل ليس الحفظ؛ اطبع ذلك المدى مرة وعُدّ أعضاءه، وبعدها لن تخطئ.