التحكّم في التدفّق والحلقات

حلقتا while و do-while

15 دقيقة الدرس 5 من 14

حلقتا while و do-while

تُعدّ حلقة for مثاليةً حين تعرف مسبقًا عدد التكرارات المطلوبة. لكن ماذا لو لم تعرف هذا العدد؟ ماذا لو أردت أن تستمر في طلب إدخال من المستخدم حتى يكتب رقمًا صالحًا، أو تواصل قراءة السجلات حتى تنتهي البيانات؟ هنا يبرز دور حلقتَي while وdo-while.

حلقة while — مضبوطة من البداية

تتحقق حلقة while من شرطها قبل كل تكرار. إذا كان الشرط false منذ البداية، فلن ينفّذ الجسم أبدًا.

int count = 1; while (count <= 5) { System.out.println("count = " + count); count++; } // الناتج: count = 1 count = 2 count = 3 count = 4 count = 5

للبنية ثلاثة أجزاء عليك إدارتها بنفسك:

  1. التهيئة: تهيئة المتغير قبل الحلقة (int count = 1).
  2. الفحص: اختبار الشرط في بداية كل تكرار.
  3. التحديث: تعديل المتغير داخل الجسم حتى يصبح الشرط false في نهاية المطاف.
نسيان خطوة التحديث يؤدي إلى حلقة لا نهائية. لو حُذف السطر count++ من المثال أعلاه، لظلّ الشرط count <= 5 صحيحًا إلى الأبد ويتجمّد البرنامج. تأكد دائمًا أن جسم الحلقة يتقدّم نحو شرط الخروج.

قيم الحارس (Sentinel Values)

قيمة الحارس هي مدخلة خاصة يكتبها المستخدم (أو قيمة بيانات خاصة يصادفها البرنامج) تُشير إلى "أوقف الحلقة". يُعدّ هذا من أكثر استخدامات while عملية.

import java.util.Scanner; public class SentinelDemo { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int sum = 0; System.out.println("أدخل أرقامًا للجمع. اكتب -1 للتوقف."); int input = scanner.nextInt(); // قراءة أول قيمة قبل الحلقة while (input != -1) { // -1 هي قيمة الحارس sum += input; input = scanner.nextInt(); // قراءة القيمة التالية في نهاية الجسم } System.out.println("المجموع: " + sum); scanner.close(); } }
اقرأ قبل الحلقة، ثم اقرأ مجددًا في نهاية الجسم. يُضمن هذا النمط "القراءة التمهيدية" أن يُختبر الشرط دائمًا بقيمة مُقروءة حديثًا. وهو النمط المعياري لحلقات while المضبوطة بحارس.

حلقة do-while — مضبوطة من النهاية

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

import java.util.Scanner; public class DoWhileDemo { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int number; do { System.out.print("أدخل رقمًا موجبًا: "); number = scanner.nextInt(); } while (number <= 0); // استمر في الطلب حتى يصبح المدخل صالحًا System.out.println("أدخلت: " + number); scanner.close(); } }

بدون do-while ستضطر إما لتكرار عبارة الطلب قبل الحلقة أو اللجوء إلى حلٍّ بديل. التصميم المضبوط من النهاية مناسب طبيعيًا حين يجب أن يعمل الجسم مرةً واحدة لإنتاج القيمة التي يفحصها الشرط.

while مقابل do-while — متى تستخدم كلًّا منهما؟

  • استخدم while حين قد لا تحتاج الحلقة إلى التنفيذ أصلًا (مثل معالجة ملف قد يكون فارغًا).
  • استخدم do-while حين يجب أن ينفّذ جسم الحلقة مرةً واحدة على الأقل (مثل عرض قائمة قبل فحص اختيار المستخدم).

تجنّب الحلقات اللانهائية

كل حلقة تحتاج مسار خروج مضمون. اسأل نفسك ثلاثة أسئلة قبل كتابة أي حلقة while:

  1. ما المتغير أو الشرط الذي يتحكم بالحلقة؟
  2. هل يُعدَّل ذلك المتغير داخل الجسم؟
  3. هل سيجعل ذلك التعديل الشرط false في النهاية؟
// خطر — حلقة لانهائية: الشرط لا يتغير أبدًا boolean running = true; while (running) { System.out.println("هذا لن يتوقف أبدًا!"); // running لا تُضبط على false أبدًا } // الحل — تنتهي الحلقة حين يكتب المستخدم "quit" import java.util.Scanner; Scanner sc = new Scanner(System.in); String command = ""; while (!command.equals("quit")) { System.out.print("أمر: "); command = sc.nextLine(); System.out.println("كتبت: " + command); }
أثناء التطوير، إذا وقعت في حلقة لانهائية بالخطأ، اضغط Ctrl + C في الطرفية لإيقاف البرنامج قسرًا. في بيئة التطوير المتكاملة (IDE)، انقر زر الإيقاف الأحمر. أضف عبارة طباعة داخل جسم الحلقة كفحص سريع أثناء تصحيح الأخطاء.

الخلاصة

حلقة while مضبوطة من البداية: يُفحص الشرط أولًا وقد لا ينفّذ الجسم أبدًا. أما حلقة do-while فهي مضبوطة من النهاية: ينفّذ الجسم دائمًا مرةً واحدة على الأقل. تمنح قيم الحارس حلقات while شرط خروج نظيفًا مدفوعًا بالمستخدم. تأكد دائمًا أن جسم الحلقة يتقدم نحو شرط الخروج لتفادي الحلقات اللانهائية.