الوحدة 8: حلقات التكرار (for, while, do-while)

أتمتة المهام المتكررة بكفاءة وفعالية

المدة: أسبوعان 6 ساعات نظرية 6 ساعات عملية

المقدمة والأهداف التعليمية

تُعد **الحلقات (Loops)** ركنًا أساسيًا في عالم البرمجة، فهي تمنحنا القدرة على تكرار تنفيذ مجموعة من الأوامر عددًا من المرات دون الحاجة لكتابة نفس الكود مرارًا وتكرارًا. هذه الوحدة ستأخذك في رحلة عميقة لاستكشاف الأنواع المختلفة للحلقات في لغة الجافا، وكيفية استخدامها بفاعلية لجعل برامجك أكثر كفاءة وقوة.

بعد إتقانك لجمل التحكم الشرطية في الوحدة السابقة، ستجد أن الحلقات تكمل هذا المفهوم بتمكين برامجك من أداء مهام متكررة بذكاء وديناميكية. سنتعلم متى نختار النوع المناسب من الحلقات (for، while، do-while) وكيف نتحكم في تدفقها باستخدام أدوات قوية مثل break و continue.

الأهداف:

بنهاية هذه الوحدة، ستكون قادرًا على:

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

أولاً: مقدمة عن الحلقات في البرمجة

تخيل أن لديك مهمة تتطلب تكرار نفس العملية عدة مرات، مثلاً: طباعة الأعداد من 1 إلى 100، أو جمع درجات 50 طالبًا، أو معالجة كل ملف في مجلد معين. بدون الحلقات، ستضطر إلى كتابة سطر كود لكل عملية، مما سيجعل الكود طويلًا، صعب القراءة، وأكثر عرضة للأخطاء. هنا تكمن قوة **الحلقات (Loops)**.

**الحلقات (Loops)** هي هياكل تحكم في البرمجة تسمح بتكرار تنفيذ مجموعة معينة من التعليمات البرمجية طالما أن شرطًا معينًا يظل صحيحًا، أو لعدد محدد من المرات. إنها تُعد من اللبنات الأساسية في بناء برامج فعالة، ديناميكية، ومختصرة.

لماذا نحتاج الحلقات؟
  • **الاختصار:** تقلل حجم الكود بشكل كبير.
  • **المرونة:** تمكنك من معالجة كميات مختلفة من البيانات بنفس الكود.
  • **الكفاءة:** تجعل البرنامج ينفذ المهام المتكررة بسرعة.
  • **سهولة الصيانة:** التعديل على الكود يصبح أسهل بكثير.
---

ثانيًا: أنواع الحلقات في جافا

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

1. الحلقة `for`

حلقة for هي الخيار الأمثل عندما يكون **عدد مرات التكرار معروفًا مسبقًا**. على سبيل المثال، إذا كنت تريد طباعة قائمة بأسماء الطلاب، أو حساب مجموع 10 أرقام، فإن for هي الأنسب. تتميز حلقة `for` بأنها تجمع جميع عناصر التحكم في التكرار (التهيئة، الشرط، والتحديث) في سطر واحد، مما يجعلها أنيقة ومنظمة.

بنيتها الأساسية:

for (initialization; condition; update) {
    // مجموعة الأوامر التي سيتم تكرار تنفيذها
}
  • **`initialization` (تهيئة المتغير):** يتم تنفيذ هذا الجزء **مرة واحدة فقط** في بداية الحلقة. يُستخدم لتهيئة (إعطاء قيمة ابتدائية) متغير التحكم في الحلقة. يمكنك تعريف المتغير هنا أيضًا.
  • **`condition` (الشرط):** يتم تقييم هذا التعبير المنطقي **قبل كل دورة** من دورات الحلقة. إذا كانت النتيجة true، تستمر الحلقة في التنفيذ. إذا أصبحت false، تتوقف الحلقة وينتقل التحكم إلى الجملة التي تليها مباشرة.
  • **`update` (التحديث):** يتم تنفيذ هذا الجزء **بعد كل دورة كاملة** للحلقة (أي بعد تنفيذ الأوامر داخل الكتلة). يُستخدم لتعديل قيمة متغير التحكم (مثل زيادته أو إنقاصه) بحيث يؤثر على الشرط في الدورات اللاحقة.
مثال 1: طباعة الأعداد من 1 إلى 5
public class ForLoopNumbers {
    public static void main(String[] args) {
        System.out.println("طباعة الأعداد باستخدام for:");
        for (int i = 1; i <= 5; i++) {
            System.out.println(i);
        }
    }
}

الشرح:

  • int i = 1;: يبدأ العداد i من القيمة 1 (تهيئة).
  • i <= 5;: الشرط هو أن يستمر التكرار طالما أن i أقل من أو يساوي 5.
  • i++: بعد كل دورة، تزداد قيمة i بمقدار 1 (تحديث).

الناتج المتوقع:

طباعة الأعداد باستخدام for:
1
2
3
4
5

2. الحلقة `while`

حلقة while هي الأكثر مرونة، وتُستخدم عندما **لا نعرف عدد مرات التكرار مسبقًا**، ولكن لدينا شرط معين يجب أن يتحقق لكي تستمر الحلقة. طالما أن الشرط صحيح، تستمر الحلقة في العمل. غالبًا ما تُستخدم في سيناريوهات مثل قراءة البيانات حتى تصل إلى نهاية ملف، أو الاستمرار في طلب إدخال من المستخدم حتى يُدخل قيمة صحيحة.

بنيتها الأساسية:

while (condition) {
    // مجموعة الأوامر التي سيتم تكرار تنفيذها
    // يجب أن يكون هناك كود داخل الكتلة يؤثر على الشرط ليصبح false في النهاية،
    // وإلا ستكون حلقة لا نهائية (infinite loop)
}
مثال 2: طباعة الأعداد من 1 إلى 5
public class WhileLoopNumbers {
    public static void main(String[] args) {
        System.out.println("طباعة الأعداد باستخدام while:");
        int i = 1; // تهيئة المتغير خارج الحلقة
        while (i <= 5) { // الشرط
            System.out.println(i);
            i++; // تحديث المتغير داخل الحلقة
        }
    }
}

الشرح: يتم تهيئة `i` قبل الحلقة. يتم فحص الشرط (`i <= 5`) في بداية كل دورة. إذا كان صحيحًا، تُنفذ الأوامر ثم يتم تحديث `i`. هذا الترتيب يعني أن الكود داخل الحلقة قد لا يُنفذ أبدًا إذا كان الشرط خاطئًا من البداية.

الناتج المتوقع:

طباعة الأعداد باستخدام while:
1
2
3
4
5

3. الحلقة `do-while`

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

بنيتها الأساسية:

do {
    // مجموعة الأوامر التي سيتم تكرار تنفيذها
    // تُنفذ هذه الأوامر مرة واحدة على الأقل
} while (condition); // يتم تقييم الشرط في نهاية الدورة
مثال 3: ضمان إدخال رقم موجب
import java.util.Scanner;

public class DoWhilePositiveNumber {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int number;

        System.out.println("سنطلب منك إدخال رقم موجب.");
        do {
            System.out.print("أدخل رقمًا موجبًا: ");
            number = input.nextInt();
            if (number <= 0) {
                System.out.println("الرقم الذي أدخلته غير موجب، يرجى المحاولة مرة أخرى.");
            }
        } while (number <= 0); // ستستمر الحلقة طالما أن الرقم ليس موجباً

        System.out.println("شكرًا لك! لقد أدخلت الرقم الموجب: " + number);
        input.close();
    }
}

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

---

ثالثًا: الفروق الجوهرية بين أنواع الحلقات

اختيار الحلقة الصحيحة يعتمد على السيناريو الذي تعمل عليه. فهم الفروقات الرئيسية سيساعدك على اتخاذ القرار الأمثل:

نوع الحلقة متى تُستخدم (الحالات الشائعة) السمات الرئيسية والمميزات
for
  • عندما يكون **عدد التكرارات معروفًا ومحددًا** مسبقًا.
  • المرور على عناصر مصفوفة أو قائمة بحجم ثابت.
  • تكرار عملية لعدد معين من المرات.
  • **بنية مدمجة:** التهيئة والشرط والتحديث في سطر واحد، مما يجعلها أنيقة وواضحة للحلقات المحدودة.
  • **سهلة التحكم:** غالبًا ما تكون سهلة الفهم وإدارة متغيرات العداد.
  • **الكود القابل للقراءة:** تبدو منظمة جدًا للمبرمجين.
while
  • عندما يكون **الشرط معروفًا لكن عدد التكرارات غير معروف** مسبقًا.
  • الاستمرار في العمليات حتى يتم استيفاء شرط معين (مثل قراءة بيانات من ملف حتى نهايته).
  • انتظار إدخال معين من المستخدم.
  • **اختبار الشرط أولاً:** لا يُنفذ جسم الحلقة أبدًا إذا كان الشرط خاطئًا من البداية.
  • **مرونة عالية:** مناسبة للمواقف التي يتغير فيها الشرط بشكل ديناميكي داخل جسم الحلقة.
  • **أبسط بنية:** تتكون فقط من الشرط داخل القوسين، مما يجعلها بسيطة للمنطق الشرطي المستمر.
do-while
  • عندما نريد **تنفيذ الكود داخل الحلقة مرة واحدة على الأقل**، بغض النظر عن الشرط الأولي، ثم نتحقق من الشرط للاستمرار.
  • سيناريوهات التحقق من المدخلات، حيث يجب طلب المدخلات مرة واحدة على الأقل.
  • قوائم الخيارات التي يجب عرضها للمستخدم لمرة واحدة على الأقل.
  • **تضمن التنفيذ الأول:** تُعد هذه الميزة هي الفارق الجوهري، حيث تضمن تنفيذ الكتلة البرمجية لمرة واحدة على الأقل.
  • **اختبار الشرط لاحقًا:** يتم فحص الشرط في نهاية الدورة.
نصيحة إضافية:

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

---

رابعًا: التحكم في تدفق الحلقات باستخدام `continue` و `break`

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

  • **`break`:** هذه الجملة القوية تُستخدم للخروج **تمامًا** من الحلقة التي تتواجد بداخلها. بمجرد أن يواجه المترجم جملة break، يتوقف تنفيذ الحلقة فورًا، وينتقل التحكم إلى أول جملة كود تلي الحلقة مباشرةً. إنها مفيدة لإنهاء الحلقة مبكرًا عند استيفاء شرط معين لا يتطلب استكمال جميع التكرارات.
  • **`continue`:** على عكس break التي تنهي الحلقة، جملة continue تُستخدم لتجاوز الدورة **الحالية** من الحلقة والانتقال مباشرةً إلى الدورة التالية. يتم تجاهل أي تعليمات برمجية موجودة بعد continue في الدورة الحالية، وتنتقل الحلقة مباشرةً إلى تحديث متغير التحكم (في حالة for) أو فحص الشرط (في حالة while و do-while) لبدء الدورة الجديدة.
مثال 1: استخدام `continue` لتجاوز الأعداد الزوجية
public class ContinueExample {
    public static void main(String[] args) {
        System.out.println("طباعة الأعداد الفردية فقط من 1 إلى 10:");
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) { // إذا كان العدد زوجيًا (باقي القسمة على 2 يساوي 0)
                continue;  // تجاوز هذه الدورة وانتقل للدورة التالية مباشرة
            }
            System.out.println(i); // هذا السطر لن يُنفذ إذا كان i زوجيًا
        }
    }
}

الناتج المتوقع:

طباعة الأعداد الفردية فقط من 1 إلى 10:
1
3
5
7
9
مثال 2: استخدام `break` للخروج المبكر من الحلقة
public class BreakExample {
    public static void main(String[] args) {
        System.out.println("البحث عن أول رقم يقبل القسمة على 7 من 1 إلى 20:");
        for (int i = 1; i <= 20; i++) {
            if (i % 7 == 0) { // إذا كان العدد يقبل القسمة على 7
                System.out.println("تم العثور على أول رقم يقبل القسمة على 7: " + i);
                break;  // اخرج تمامًا من الحلقة فوراً
            }
        }
        System.out.println("انتهى البحث.");
    }
}

الناتج المتوقع:

البحث عن أول رقم يقبل القسمة على 7 من 1 إلى 20:
تم العثور على أول رقم يقبل القسمة على 7: 7
انتهى البحث.
---

خامسًا: أمثلة عملية وتطبيقية

لنُطبق ما تعلمناه في أمثلة برمجية متنوعة تعكس استخدامات الحلقات في سيناريوهات واقعية.

المثال 1: حساب مجموع الأعداد من 1 إلى N (باستخدام `for`)

اكتب برنامجًا يطلب من المستخدم إدخال عدد صحيح موجب (N)، ثم يحسب ويطبع مجموع الأعداد من 1 إلى N.

import java.util.Scanner;

public class SumOfNNumbers {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n;
        long sum = 0; // استخدم long لتجنب تجاوز سعة int للأعداد الكبيرة

        System.out.print("أدخل عددًا صحيحًا موجبًا (N) لحساب مجموع الأعداد من 1 إلى N: ");
        n = scanner.nextInt();

        if (n < 1) {
            System.out.println("الرجاء إدخال عدد موجب.");
        } else {
            for (int i = 1; i <= n; i++) {
                sum += i; // اختصار لـ sum = sum + i;
            }
            System.out.println("مجموع الأعداد من 1 إلى " + n + " هو: " + sum);
        }
        scanner.close();
    }
}

مثال على الإدخال والناتج:

أدخل عددًا صحيحًا موجبًا (N) لحساب مجموع الأعداد من 1 إلى N: 10
مجموع الأعداد من 1 إلى 10 هو: 55

المثال 2: التحقق من صحة إدخال المستخدم (باستخدام `do-while`)

اكتب برنامجًا يطلب من المستخدم إدخال كلمة مرور. يجب أن تكون كلمة المرور "secret123" فقط. استمر في طلب كلمة المرور حتى يُدخلها المستخدم بشكل صحيح.

import java.util.Scanner;

public class PasswordValidator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String password;
        String correctPassword = "secret123";

        System.out.println("برنامج التحقق من كلمة المرور.");
        do {
            System.out.print("الرجاء إدخال كلمة المرور: ");
            password = scanner.nextLine();

            if (!password.equals(correctPassword)) {
                System.out.println("كلمة المرور خاطئة. يرجى المحاولة مرة أخرى.");
            }
        } while (!password.equals(correctPassword)); // الشرط: استمر طالما كلمة المرور غير صحيحة

        System.out.println("تهانينا! لقد أدخلت كلمة المرور الصحيحة. مرحباً بك!");
        scanner.close();
    }
}

مثال على الإدخال والناتج:

برنامج التحقق من كلمة المرور.
الرجاء إدخال كلمة المرور: wrong
كلمة المرور خاطئة. يرجى المحاولة مرة أخرى.
الرجاء إدخال كلمة المرور: password
كلمة المرور خاطئة. يرجى المحاولة مرة أخرى.
الرجاء إدخال كلمة المرور: secret123
تهانينا! لقد أدخلت كلمة المرور الصحيحة. مرحباً بك!

المثال 3: عرض قائمة خيارات حتى يختار المستخدم "الخروج" (باستخدام `while`)

اكتب برنامجًا يعرض قائمة من الخيارات للمستخدم (مثل 1: عرض، 2: إضافة، 3: حذف، 4: خروج) ويستمر في عرض القائمة وتلقي المدخلات حتى يختار المستخدم الخيار "4" (الخروج).

import java.util.Scanner;

public class MenuDrivenProgram {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int choice = 0; // تهيئة بـ 0 لضمان دخول الحلقة لأول مرة

        while (choice != 4) { // استمر طالما أن المستخدم لم يختر الخروج
            System.out.println("\n--- قائمة الخيارات ---");
            System.out.println("1. عرض البيانات");
            System.out.println("2. إضافة سجل جديد");
            System.out.println("3. حذف سجل");
            System.out.println("4. الخروج من البرنامج");
            System.out.print("الرجاء اختيار رقم: ");
            
            choice = scanner.nextInt(); // قراءة اختيار المستخدم

            switch (choice) {
                case 1:
                    System.out.println(">>> يتم عرض البيانات...");
                    break;
                case 2:
                    System.out.println(">>> يتم إضافة سجل جديد...");
                    break;
                case 3:
                    System.out.println(">>> يتم حذف سجل...");
                    break;
                case 4:
                    System.out.println(">>> شكرًا لاستخدامك البرنامج. إلى اللقاء!");
                    break;
                default:
                    System.out.println(">>> خيار غير صالح. يرجى اختيار رقم من 1 إلى 4.");
            }
        }
        scanner.close();
    }
}

مثال على الإدخال والناتج:

--- قائمة الخيارات ---
1. عرض البيانات
2. إضافة سجل جديد
3. حذف سجل
4. الخروج من البرنامج
الرجاء اختيار رقم: 1
>>> يتم عرض البيانات...

--- قائمة الخيارات ---
1. عرض البيانات
2. إضافة سجل جديد
3. حذف سجل
4. الخروج من البرنامج
الرجاء اختيار رقم: 5
>>> خيار غير صالح. يرجى اختيار رقم من 1 إلى 4.

--- قائمة الخيارات ---
1. عرض البيانات
2. إضافة سجل جديد
3. حذف سجل
4. الخروج من البرنامج
الرجاء اختيار رقم: 4
>>> شكرًا لاستخدامك البرنامج. إلى اللقاء!
---

سادسًا: تمارين وتحديات

اختبر فهمك لهذه الوحدة من خلال حل التمارين التالية:

1. تمرين: حساب العدد العاملي (Factorial) (باستخدام `for` أو `while`)

اكتب برنامج جافا يطلب من المستخدم إدخال عدد صحيح موجب. قم بحساب العدد العاملي (Factorial) لهذا العدد واطبعه. العدد العاملي للعدد $N$ (يُرمز له بـ $N!$) هو حاصل ضرب جميع الأعداد الصحيحة الموجبة الأقل من أو تساوي $N$. على سبيل المثال، $5! = 5 \times 4 \times 3 \times 2 \times 1 = 120$.

import java.util.Scanner;

public class FactorialCalculator {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // اكتب الكود هنا لحساب العدد العاملي
        scanner.close();
    }
}

2. تمرين: طباعة هرم من النجوم (باستخدام حلقات متداخلة)

اكتب برنامجًا يطلب من المستخدم إدخال عدد الصفوف (مثل 5). ثم اطبع هرمًا من النجوم على الكونسول بهذا العدد من الصفوف.
مثال لهرم بـ 5 صفوف:

*
**
***
****
*****

تلميح: ستحتاج إلى حلقات متداخلة (Nested Loops) حيث تتحكم الحلقة الخارجية في عدد الصفوف، والحلقة الداخلية في عدد النجوم في كل صف.

import java.util.Scanner;

public class StarPyramid {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // اكتب الكود هنا لطباعة هرم من النجوم
        scanner.close();
    }
}

3. تمرين تحدي: تخمين الرقم السري (باستخدام `while` و `break`)

اكتب برنامجًا يحدد رقمًا سريًا ثابتًا (مثلاً 42). اطلب من المستخدم تخمين الرقم. استمر في طلب التخمين حتى يقوم المستخدم بتخمين الرقم الصحيح.

  • إذا كان التخمين أعلى من الرقم السري، اطبع "أعلى من اللازم، حاول مرة أخرى!".
  • إذا كان التخمين أقل من الرقم السري، اطبع "أقل من اللازم، حاول مرة أخرى!".
  • إذا كان التخمين صحيحًا، اطبع "تهانينا! لقد خمنت الرقم الصحيح!" واخرج من اللعبة.
تتبع عدد محاولات المستخدم واطبعه في النهاية.

import java.util.Scanner;
import java.util.Random; // لتوليد رقم عشوائي إذا أردت الرقم السري متغيرًا

public class GuessTheNumber {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // int secretNumber = new Random().nextInt(100) + 1; // رقم سري عشوائي بين 1 و 100
        int secretNumber = 42; // رقم سري ثابت
        int guess;
        int attempts = 0;

        System.out.println("مرحباً بك في لعبة تخمين الرقم السري!");
        System.out.println("أنا أفكر في رقم بين 1 و 100.");

        // اكتب الكود هنا للعبة التخمين باستخدام حلقة while
        
        scanner.close();
    }
}
---

سابعًا: الخلاصة

لقد اكتشفت في هذه الوحدة الحاسمة قوة ومرونة **الحلقات (Loops)** في Java، وهي أدوات لا غنى عنها لأي مبرمج يسعى لكتابة برامج فعالة ومختصرة.

  • تعلمنا أن الحلقات توفر الوقت والجهد وتختصر الكود بشكل كبير عن طريق أتمتة المهام المتكررة.
  • تعرفنا على الأنواع الثلاثة الرئيسية للحلقات في Java:
    • for: مثالية عندما يكون عدد التكرارات معروفًا مسبقًا.
    • while: تُستخدم عندما يكون الشرط معروفًا ولكن عدد التكرارات غير محدد مسبقًا.
    • do-while: تضمن تنفيذ الكود مرة واحدة على الأقل قبل التحقق من الشرط، وهي مفيدة لسيناريوهات التحقق من المدخلات.
  • استكشفنا العبارتين break و continue التي تمنحنا مرونة أكبر للتحكم في تدفق الحلقة، سواء بالخروج منها تمامًا أو بتخطي الدورة الحالية.

إن إتقان الحلقات هو خطوة أساسية نحو كتابة برامج أكثر كفاءة وتعقيدًا. تذكر أن التدريب العملي على أمثلة متنوعة هو أفضل وسيلة لإتقان استخدام الحلقات بشكل فعال. في الوحدة القادمة، سنتعمق في الدوال (Methods) وكيفية تنظيم الكود الخاص بك في كتل قابلة لإعادة الاستخدام.

المراجع

  • موقع Oracle Java Documentation.
  • "Java: A Beginner's Guide" – Herbert Schildt.
  • "Head First Java" – Kathy Sierra, Bert Bates.