الـ Loops، عمليات تكرار الكود في البرمجة

السلام عليكم ورحمة الله وبركاته

وقت القراءة: ≈ 25 دقيقة (بمعدل فنجان واحد من الشاي 😊)

المقدمة

سؤال سريع لو قلت لك أن تكتب برنامج يطبع الأرقام من 1 إلى 5، كيف ستقوم بذلك ؟
الطريقة الأولى التي قد تتبادر إلى ذهنك هي كتابة الأرقام يدوياً:

cout << 1 << '\n';
cout << 2 << '\n';
cout << 3 << '\n';
cout << 4 << '\n';
cout << 5 << '\n';

حسنًا جيد ماذا عن من 1 إلى 10 ؟

cout << 1 << '\n';
cout << 2 << '\n';
cout << 3 << '\n';
cout << 4 << '\n';
cout << 5 << '\n';
cout << 6 << '\n';
cout << 7 << '\n';
cout << 8 << '\n';
cout << 9 << '\n';
cout << 10 << '\n';

حسنًا ... ماذا عن من 1 إلى 100 ؟ أو انتظر لنجرب من 1 إلى 1000، ما رأيك ؟
الأمر يبدو مملًا مكررًا وأيضًا يحتاج إلى وقت طويل للكتابة
لا تعتقد أن هناك شخص عاقل سيقوم بكتابة الأرقام من 1 إلى 1000 بهذه الطريقة أو من 1 إلى 10000

أكيد أصحاب ومخترعي لغات البرمجة قدموا لنا وسيلة تساعدنا في تكرار كود ما أومجموعة من الأكواد والأوامر
وبالفعل يوجد وسائل كثيرة لتكرار الكود في البرمجة وهي تسمى بالـ Loops ومعناها حلقات تكرارية

أنواع الـ Loops

هناك العديد من الطرق لتكرار مجموعة من الأكواد واشهرها هي:

  1. while
  2. do-while
  3. for
  4. for-each أو range-based for (يوجد اشكال ومسميات مختلفة لها في كل لغة)
  5. recursion
  6. ... وغيرها وكل لغة لديها وسائل خاصة بها للتكرار

كل لغة برمجة تحتوي على أوامر تكرارية تساعد المبرمجين على تكرار الأكواد بشكل متكرر وبسهولة
سنشرح هنا الطرق الأساسية للتكرار والتي ستصادفك في جميع لغات البرمجة بنفس الشكل وطريقة التنفيذ وهي while و do-while و for

وسنشرح الـ for-each لكن في مقالة منفصلة عندما يحين وقتها وهناك مقالة مفصلة عن الـ recursion ستجدها في المدونة لكن لا تقرأها قبل أن تنتهي من سلسلة المقالات عن أساسيات البرمجة

الـ while loop

في لغات البرمجة نكتب الـ while بالشكل التالي:

while (الشرط) {
    // الكود المراد تكراره
}

ستلاحظ تشابه بين الـ while والـ if، فالشرط الذي نكتبه داخل الأقواس يكون مثل الشرط في الـ if أي قيمة تعطينا boolean أي قيمة true أول false
ولكن الفرق أن الـ if تنفذ ما بداخلها مرة واحدة فقط لو الشرط تحقق وأعطانا true
أما الـ while يقوم بتكرار الكود ويظل تنفذ ما بداخله طالما الشرط يعطينا true


int i = 0;
if (i < 5) {
    cout << "i is: " << i << '\n';
}

في هذا المثال، سيتم طباعة قيمة i إذا كانت أقل من 5
بالتالي قيمة i حاليًا بـ 0 فسيتم تنفيذ الشرط المكتوب في الـ if وطالما الشرط يعطينا true
فسيتم طباعة i is: 0 وبعدها يتوقف التنفيذ ويخرج من الـ if

أما في حالة الـ while:

int i = 0;
while (i < 5) {
    cout << "i is: " << i << '\n';
}

سيتم طباعة قيمة i طالما هي أقل من 5
بالتالي بسبب أن قيمة i تساوي 0 والشرط الذي كتبناه داخل الـ while يعطينا true
سيتم تنفيذ الكود المكتوب داخل الـ while وسيتم طباعة i is: 0

ثم ... ثم .. هل تستطيع التخمين ماذا سيحدث بعد ذلك ؟
بالتأكيد سيتم تكرار الكود المكتوب داخل الـ while مرة أخرى وسيتم طباعة i is: 0 مرة أخرى
لأن الشرط لازال يعطينا true في كل مرة يتم فيها تنفيذ الكود داخل الـ while
والـ while كما قلنا تظل تنفذ الكود داخلها طالما الشرط يعطينا true

وهكذا سيتم طباعة i is: 0 بشكل لا نهائي هكذا:

i is: 0
i is: 0
i is: 0
i is: 0
i is: 0
i is: 0
i is: 0
i is: 0
i is: 0
i is: 0
... etc. (infinity loop)

إذا ما الحل ؟

الحل بسيط وهو تغيير قيمة i داخل الـ while بعد كل مرة يتم فيها تنفيذ الـ while
بالتالي نضمن أن الشرط سيعطينا false في النهاية ويتوقف التنفيذ

int i = 0;
while (i < 5) {
    cout << "i is: " << i << '\n';
    i = i + 1; // or i++ or i += 1
}

لاحظ أننا نقوم بزيادة قيمة i بمقدار 1 في كل مرة يتم فيها تنفيذ الـ while
بالتالي نضمن أن الشرط i < 5 سيعطينا false بعد 5 مرات تنفيذ للـ while ويتوقف التنفيذ
وبالتالي سيتم طباعة الأرقام من 0 إلى 4 فقط

i is: 0
i is: 1
i is: 2
i is: 3
i is: 4

لنحلل الكود:

قيمة i الشرط i < 5 النتيجة
0 الشرط 0 < 5
سيعطي true
سينفذ ما بداخل الـ while ويطبع 0
ثم يقوم بزيادة قيمة i ليصبح 1
1 الشرط 1 < 5
سيعطي true
سينفذ ما بداخل الـ while ويطبع 1
ثم يقوم بزيادة قيمة i ليصبح 2
2 الشرط 2 < 5
سيعطي true
سينفذ ما بداخل الـ while ويطبع 2
ثم يقوم بزيادة قيمة i ليصبح 3
3 الشرط 3 < 5
سيعطي true
سينفذ ما بداخل الـ while ويطبع 3
ثم يقوم بزيادة قيمة i ليصبح 4
4 الشرط 4 < 5
سيعطي true
سينفذ ما بداخل الـ while ويطبع 4
ثم يقوم بزيادة قيمة i ليصبح 5
5 الشرط 5 < 5
سيعطي false
لن ينفذ ما بداخل الـ while ويتوقف التنفيذ

أمثلة على الـ while loop

لنرجع لسؤالنا الأول اطبع لي الأرقام من 1 إلى 100000

هل ستقوم بكتابة الأرقام يدويًا ؟ أم ستستخدم الـ while ؟ بالتأكيد ستستخدم الـ while

int i = 1;
while (i <= 100000) {
    cout << i << '\n';
    i++;
}

انتهينا!!
أنظر إلى الجمال والسهولة في كتابة الأكواد والتكرار بدون عناء


هل يمكنك كتابة جدول الضرب الخاص بالعدد 8 ؟ هل يمكنك أن تفكر كيف ستساعدك الـ while في ذلك ؟

int i = 0;
while (i <= 12) {
    cout << i << " × 8 = " << i * 8 << '\n';
    i++;
}

النتيجة:

0 × 8 = 0
1 × 8 = 8
2 × 8 = 16
3 × 8 = 24
4 × 8 = 32
5 × 8 = 40
6 × 8 = 48
7 × 8 = 56
8 × 8 = 64
9 × 8 = 72
10 × 8 = 80
11 × 8 = 88
12 × 8 = 96

ما رأيك ؟

لنحلل الكود:

قيمة i الشرط i <= 12 النتيجة
0 الشرط 0 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 0
ثم يقوم بزيادة قيمة i ليصبح 1
1 الشرط 1 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 1
ثم يقوم بزيادة قيمة i ليصبح 2
2 الشرط 2 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 2
ثم يقوم بزيادة قيمة i ليصبح 3
3 الشرط 3 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 3
ثم يقوم بزيادة قيمة i ليصبح 4
4 الشرط 4 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 4
ثم يقوم بزيادة قيمة i ليصبح 5
5 الشرط 5 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 5
ثم يقوم بزيادة قيمة i ليصبح 6
6 الشرط 6 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 6
ثم يقوم بزيادة قيمة i ليصبح 7
7 الشرط 7 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 7
ثم يقوم بزيادة قيمة i ليصبح 8
8 الشرط 8 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 8
ثم يقوم بزيادة قيمة i ليصبح 9
9 الشرط 9 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 9
ثم يقوم بزيادة قيمة i ليصبح 10
10 الشرط 10 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 10
ثم يقوم بزيادة قيمة i ليصبح 11
11 الشرط 11 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 11
ثم يقوم بزيادة قيمة i ليصبح 12
12 الشرط 12 <= 12
سيعطي true
سينفذ ما بداخل الـ while ويطبع 12
ثم يقوم بزيادة قيمة i ليصبح 13
13 الشرط 13 <= 12
سيعطي false
لن ينفذ ما بداخل الـ while ويتوقف التنفيذ

سؤال هل يمكنك جمع الأرقام من 1 إلى 100 ؟
أظن أنه سؤال جميل وممتع ويحتاج إلى القليل من التفكير

أكيد لن نقوم بكتابة الأرقام يدويًا ونقوم بجمعها هكذا:

int sum = 0;
sum = 1 + 2 + 3 + 4 + 5 + 6 + 7 + ... + 100;

نريد أن نستخدم الـ while لجمع الأرقام من 1 إلى 100 وطباعة الناتج النهائي
لذا نحتاج لمتغير يبدأ قيمته من 1 ويزيد بمقدار 1 كل مرة
وفي كل مرة نقوم بجمع في متغير آخر ليحتفظ بالناتج النهائي

فلنفترض أن المتغير يسمى i الذي سيبدأ من 1 ويزيد بمقدار 1 كل مرة
فأول مرة سيكون المتغير i = 1 ثم نقوم بجمع 1 في المتغير sum ويصبح sum = 1
ثم يتم زيادة قيمة i بمقدار 1 ليصبح i = 2 ثم نقوم بجمع 2 في sum ويصبح sum = 3
ثم يتم زيادة قيمة i بمقدار 1 ليصبح i = 3 ثم نقوم بجمع 3 في sum ويصبح sum = 6
وهكذا حتى يصل i إلى 100 ويتوقف التنفيذ ويتم طباعة الناتج النهائي

int i = 1;
int sum = 0;

while (i <= 100) {
    sum += i;
    i++;
}

cout << "The sum of numbers from 1 to 100 is: " << sum << '\n';

النتيجة:

The sum of numbers from 1 to 100 is: 5050

ما رأيك ؟ ما الذي حصل هنا ؟

ما حصل هو ما كنا نتحدث عنه سابقًا ببساطة قمنا بتعريف متغير i بقيمة 1
ثم قمنا بتعريف متغير sum بقيمة 0 وهو الذي سيحتفظ بالناتج النهائي

ثم كل ما نحتاجه هو جمع المتغير i في المتغير sum ثم نزيد قيمة المتغير i بمقدار 1 ونعيد جمعه مرة أخرى في sum
بحيث أن في المرة عندما يكون i = 1 سيتم جمع 1 في sum ويصبح sum = 1
ثم يتم زيادة قيمة i بمقدار 1 ليصبح i = 2 ويتم جمع 2 في sum ويصبح sum = 3
ثم يتم زيادة قيمة i بمقدار 1 ليصبح i = 3 ويتم جمع 3 في sum ويصبح sum = 6
وهكذا حتى يصل i إلى 100 ويتوقف التنفيذ ويتم طباعة الناتج النهائي

لذا كل ما كنا نحتاجه هو تكرار تلك العملية لذا استخدمنا الـ while لتكرار عملية زيادة قيمة i بمقدار 1 وجمعه مع sum حتى يصل i إلى 100


سؤال أخير ما رأيك بجمع الأرقام الزوجية فقط التي تقع بين 1 و 100 ؟
هل يمكنك حل هذا السؤال ؟

الأمر مثله مثل المثال السابق مع تغير بسيط جدًا

int i = 2;
int sum = 0;

while (i <= 100) {
    sum += i;
    i += 2;
}

cout << "The sum of even numbers from 1 to 100 is: " << sum << '\n';

النتيجة:

The sum of even numbers from 1 to 100 is: 2550

لاحظ أنه نفس الحل السابق تمامًا ولكن الفرق أننا جعلنا i يبدأ من 2 ثم نقوم بزيادة قيمة i بمقدار 2 بدلًا من 1
وهذا يعني أننا سنقوم بجمع الأرقام الزوجية فقط وليس جميع الأرقام لأن قيمة i تزيد بمقدار 2 في كل مرة
بالتالي أول مرة سيكون i = 2 ثم i = 4 ثم i = 6 وهكذا حتى يصل i إلى 100 ويتوقف التنفيذ
وكل مرة يتم جمع قيمة i في sum

ماذا عن الأرقام الفردية ؟ أظنك تستطيع حلها بنفس الطريقة السابقة دون حتى أن اشرحها لك

الـ do-while loop

الـ do-while تشبه الـ while ولكن الفرق الوحيد بينهما هو أن الـ do-while يضمن لنا تنفيذ الكود داخله مرة واحدة على الأقل
لأنه يقوم بتنفيذ الكود داخله ثم يقوم بالتحقق من الشرط
أما الـ while كانت تقوم بالتحقق من الشرط ثم تقوم بتنفيذ الكود داخله

do {
    // الكود المراد تكراره
} while (الشرط);

لاحظ أننا نكتب الـ do ثم الكود المراد تكراره ثم الـ while والشرط
وهذا يعني أن الكود المكتوب داخل الـ do سيتم تنفيذه مرة واحدة على الأقل ثم يتم التحقق من الشرط
وإذا كان الشرط يعطينا true سيتم تنفيذ الكود مرة أخرى وهكذا حتى يعطينا الشرط false

الأمر مثله مثل الـ while ولكن الـ do-while يضمن لنا تنفيذ الكود مرة واحدة على الأقل حتى لو كان الشرط يعطينا false من البداية

do {
    cout << "I will be printed once" << '\n';
} while (false);

في هذا المثال سيتم طباعة الجملة I will be printed once مرة واحدة فقط
برغم أن الشرط الذي داخل الـ while قيمته false

while (false) {
    cout << "I will not be printed" << '\n';
}

أما في هذا المثال فلن يتم طباعة الجملة I will not be printed أبدًا
لأن الشرط الذي داخل الـ while قيمته false ولن يتم تنفيذ الكود داخله
لكن في الـ do-while كان يضمن لنا تنفيذ الكود مرة واحدة على الأقل حتى لو كان الشرط يعطينا false من البداية


لنفترض مثال بسيط جدًا لنفترض أنك تريد أن تجمع رقمين موجبين فقط
وتطلب من المستخدم إدخال رقمين وتقوم بجمعهما وطباعة الناتج

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

هذا تمامًا ما يقوم به الـ do-while يقوم بتنفيذ الكود مرة واحدة على الأقل ثم يقوم بالتحقق من الشرط ثم يظل يعيد تنفيذ الكود حتى يعطينا الشرط false

int num1, num2;

do {
    cout << "Enter the first positive number: ";
    cin >> num1; // قراءة الرقم الأول من المستخدم

    cout << "Enter the second positive number: ";
    cin >> num2; // قراءة الرقم الثاني من المستخدم
} while (num1 <= 0 || num2 <= 0);

cout << "The sum of " << num1 << " and " << num2 << " is: " << num1 + num2 << '\n';

لاحظ هنا أننا عرفنا متغيرين num1 و num2
ثم قمنا بطلب من المستخدم إدخال الرقمين num1 و num2
ثم لاحظ أننا قمنا بالتحقق من الشرط num1 <= 0 || num2 <= 0 أي هل أحد الرقمين سالبًا أم لا
وإذا كان أحد الرقمين سالبًا سيتم طلب إعادة إدخال الرقمين مرة أخرى
وهكذا حتى يدخل المستخدم رقمين موجبين فقط

ثم بعد ذلك سيتم طباعة الناتج النهائي لجمع الرقمين

لاحظ جيدًا أننا استخدمنا الـ do-while لسؤال المستخدم أن يدخل الرقمين مرة واحدة على الأقل ثم نقوم بتحقق من الشرط كخطوة ثانية لتأكيد أن الرقمين موجبين فقط أم لا

هل تستطيع أن تفعل ذلك بالـ while نعم لكن سيحدث تكرار غير ضروري للكود ولن يكون منطقيًا

int num1, num2;

cout << "Enter the first positive number: ";
cin >> num1;

cout << "Enter the second positive number: ";
cin >> num2;

while (num1 <= 0 || num2 <= 0) {
    cout << "Enter the first positive number: ";
    cin >> num1;

    cout << "Enter the second positive number: ";
    cin >> num2;
}

cout << "The sum of " << num1 << " and " << num2 << " is: " << num1 + num2 << '\n';

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

لاحظ أننا قمنا بتكرار الكود المكتوب داخل الـ while مرتين مرة خارج الـ while لأننا يجب أن نحصل على الرقمين أولًا ثم كررنا نفس الكود داخل الـ while لجعل المستخدم يعيد إدخال الرقمين إذا كان أحد الرقمين سالبًا

الـ do-while يختصر لنا الكود ويلغي التكرار الغير ضروري للكود الذي حصل في الـ while


ملحوظة: أمر cin مجرد أمر في لغة البرمجة C++ لاسناد القيم والبيانات للمتغير عن طريق المستخدم وهو فقط يستقبل القيمة الذي يدخله المستخدم ويسنده للمتغير الذي نضعه بعد علامة >>
لا تحاول التركيز معه كثيرًا هنا، فقط تعلم أنه يستقبل البيانات من المستخدم
أغلب اللغات لديها أمر إدخال مشابه له مثل input في Python أو prompt في JavaScript و cin في C++ وغيرها
فقط مجرد أوامر تستقبل القيم من المستخدم

cin >> name; // C++
scanf("%s", name); // C
name = input("Enter your name: ") // Python
name = prompt("Enter your name: ") // JavaScript
name = readline("Enter your name: ") // PHP

كل هذه الأوامر تقوم بنفس الغرض وهو استقبال قيمة من المستخدم واسنداها للمتغير الذي يدعى name

أمثلة على الـ do-while loop

لنقم بعمل مثال بسيط وهو نطلب من المستخدم ادخال اسمه
ونشترط عليه أن يبدأ الاسم بحرف A فقط

string name;

do {
    cout << "Enter your name: ";
    cin >> name;
} while (name[0] != 'A');

cout << "Welcome " << name << '\n';

هنا قمنا بتعريف متغير name من نوع string ليحتفظ بالاسم الذي يدخله المستخدم
ثم قمنا بطلب من المستخدم إدخال اسمه
ثم قمنا بالتحقق من الشرط هل يبدأ الاسم بحرف A أم لا
إذا كان يبدأ بحرف A سيتم الخروج من الـ do-while ويتم طباعة الجملة Welcome مع الاسم الذي أدخله المستخدم
إذا لم يبدأ بحرف A سيظل يعيد طلب من المستخدم أن يقوم بإدخال الاسم مرة أخرى حتى يقوم بإدخال اسم يبدأ بحرف A


لنعطي مثال آخر لنقم بطلب من المستخدم إدخال رقمين
ثم نقوم نعطيه مجموعة من الاختيارات، هل يريد جمع الرقمين أم طرحهما أم ضربهما أم قسمتهما
ونقوم بتنفيذ العملية المطلوبة وطباعة الناتج

ثم نسأله هل يريد القيام بعملية أخرى أم لا

int num1, num2, choice;

do {
    cout << "Enter the first number: ";
    cin >> num1;

    cout << "Enter the second number: ";
    cin >> num2;

    cout << "Choose the operation you want to do: \n";
    cout << "1. Add\n";
    cout << "2. Subtract\n";
    cout << "3. Multiply\n";
    cout << "4. Divide\n";
    cout << "Enter your choice: ";
    cin >> choice;

    switch (choice) {
        case 1:
            cout << "The sum of " << num1 << " and " << num2 << " is: " << num1 + num2 << '\n';
            break;
        case 2:
            cout << "The subtraction of " << num1 << " and " << num2 << " is: " << num1 - num2 << '\n';
            break;
        case 3:
            cout << "The multiplication of " << num1 << " and " << num2 << " is: " << num1 * num2 << '\n';
            break;
        case 4:
            cout << "The division of " << num1 << " and " << num2 << " is: " << num1 / num2 << '\n';
            break;
        default:
            cout << "Invalid choice\n";
    }

    cout << "Do you want to do another operation? (1 for yes, anything else for no): ";
    cin >> choice;
} while (choice == 1);

cout << "Thank you for using our calculator\n";

الكود كبير قليلًا لكن لا تقلق يمكننا تحليله
إن قرأت الكود بتمعن ستجد أنه ليس بالصعوبة التي تتخيلها
بالطبع أنت تعرف الـ switch وكيفية عمله وكيفية استخدامه

لنقم بتحليل الكود:

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

الـ for loop

الـ for هو نوع آخر من أدوات التكرار ولكنه يختلف عن الـ while والـ do-while من حيث الكتابة وطريقة التنفيذ
الـ for تحاول تقديم طريقة أسهل لتكرار الكود وتقليل الأخطاء التي قد تحدث بحيث تجمع كل شيء تريده في سطر واحد

for (1; 2; 4) {
    // 3: الكود المراد تكراره
}

تنقسم الـ for إلى أربعة أقسام مهمة:

  1. القسم الأول: نستطيع فيه تعريف المتغير الذي سنستخدمه داخل الـ loop ونستطيع اعطاء قيمة أولية له
    مثل int i = 0 هنا قمنا بتعريف متغير i من نوع int وأعطيناه قيمة ابتدائية 0
  2. القسم الثاني: هو المكان الذي نضع فيه الشرط الخاص بالـ loop وهو الشرط الذي يحدد متى يتوقف التكرار
    مثل i < 5 هنا قمنا بوضع الشرط أن يتوقف التكرار عندما يكون i < 5
  3. القسم الثالث: هو المكان الذي نقوم فيه بكتابة الكود الذي نريد تكراره
  4. القسم الرابع: هو المكان الذي نقوم فيه بزيادة قيمة المتغير الذي قمنا بتعريفه في القسم الأول
    مثل i++ هنا قمنا بزيادة قيمة المتغير i بمقدار 1 بعد كل تكرار

الـ for يقوم بتنفيذ القسم الأول أولًا مرة واحدة فقط ثم يقوم بالتحقق من القسم الثاني الذي فيه الشرط
إذا كان الشرط يعطينا true سيتم تنفيذ الكود الذي في القسم الثالث ثم ينتقل إلى القسم الرابع ليحدث قيمة المتغير
ثم يظل يكرر المرور على القسم الثاني ثم القسم الثالث ثم القسم الرابع حتى يعطينا الشرط الذي في القسم الثاني false

لنجمع ما قلناه في مثال بسيط لنقوم بطباعة الأرقام من 0 إلى 4

for (int i = 0; i < 5; i++) {
    cout << i << '\n';
}

حسنًا ركز معي جيدًا هنا
في البداية قمنا بتعريف متغير i من نوع int وأعطيناه قيمة ابتدائية 0 في القسم الأول من الـ for
وهذا التعريف يحدث مرة واحدة فقط

ثم في القسم الثاني قمنا بوضع الشرط i < 5
ثم في القسم الثالث قمنا بكتابة الكود المراد تكراره وهنا نحن فقط نقوم بطباعة قيمة المتغير i هكذا cout << i << '\n';
ثم في القسم الرابع قمنا بزيادة قيمة المتغير i بمقدار 1 بعد كل لفة هكذا i++


كيف ينتقل الـ for بين الأقسام ؟

بالتالي في الكود السابق سيتم طباعة الأرقام من 0 إلى 4
لنعد كتابته هنا مجددًا لنعلق عليه ونوضح كيف سيسير التنفيذ

for (int i = 0; i < 5; i++) {
    cout << i << '\n';
}

لاحظ شيء مهم جدًا أن الكود الذي نكتبه بالـ for نستطيع كتابته بالـ while والعكس
لكن الـ for يحاول تقديم طريقة أسهل لكتابة الكود while

لاحظ الفرق بين طريقة الكتابة بالـ for والـ while في نفس المثال

// for
for (int i = 0; i < 5; i++) {
    cout << i << '\n';
}

// while
int i = 0;
while (i < 5) {
    cout << i << '\n';
    i++;
}

كلاهما يقومان بنفس العمل ولكن الـ for يحاول تقديم طريقة أسهل لكتابة الكود وتجميع كل شيء في سطر واحد
لكن ستجد في بعض الأحيان أن الـ while أسهل لكتابة الكود وأوضح بكثير
أنت في النهاية تختار الأداة التي تجد أنها تناسبك أكثر في الموقف الذي تواجهه


أمثلة على الـ for loop

لنقم بعمل مثال بسيط وهو جمع الأرقام من 1 إلى 10

int sum = 0;

for (int i = 1; i <= 10; i++) {
    sum = sum + i;
}

cout << "The sum of numbers from 1 to 10 is: " << sum << '\n';

هنا قمنا بتعريف متغير sum وأعطيناه قيمة ابتدائية 0
ثم قمنا بالدخول للـ for وتعريف متغير i وأعطيناه قيمة ابتدائية 1
ووضعنا الشرط i <= 10 وهذا يعني أن التكرار سيتوقف عندما يكون i أكبر من 10
ثم قمنا بجمع قيمة المتغير i في المتغير sum
ثم زيادة قيمة المتغير i بمقدار 1 بعد كل تكرار

وبعد الانتهاء من التكرار سيتم طباعة قيمة المتغير sum وهي الناتج النهائي لجمع الأرقام من 1 إلى 10

قيمة i قيمة sum قبل الجمع قيمة sum بعد الجمع
sum = sum + i
1 0 1
2 1 3
3 3 6
4 6 10
5 10 15
6 15 21
7 21 28
8 28 36
9 36 45
10 45 55

وبعد الانتهاء من كل اللفات سيتم طباعة الناتج النهائي 55


لنأخذ مثال جميل نريد إيجاد مضروب العدد n أيًا كان
والمضروب هو لو كان n = 5 فإن المضروب هو 5 * 4 * 3 * 2 * 1

int n = 5;
int factorial = 1;

for (int i = 1; i <= n; i++) {
    factorial = factorial * i;
}

cout << "The factorial of " << n << " is: " << factorial << '\n';

هنا قمنا بتعريف متغير n وأعطيناه قيمة 5
ثم قمنا بتعريف متغير factorial وأعطيناه قيمة ابتدائية 1 لأننا سنقوم بعملية الضرب فيه
ثم قمنا بالدخول للـ for وتعريف متغير i وأعطيناه قيمة 1
ووضعنا الشرط i <= n وهذا يعني أن التكرار سيتوقف عندما يكون i أكبر من n

ثم قمنا بضرب قيمة المتغير i في المتغير factorial وحفظ الناتج في نفس المتغير factorial
ففي البداية factorial = 1 وi = 1 سيتم الضرب factorial = 1 * 1 ويصبح factorial = 1
ثم في اللفة الثانية factorial = 1 وi = 2 سيتم الضرب factorial = 5 * 2 ويصبح factorial = 10
وهكذا حتى يصل i إلى 5 ويتوقف التكرار

قيمة i قيمة factorial قبل الضرب قيمة factorial بعد الضرب
factorial = factorial * i
1 1 1
2 1 2
3 2 6
4 6 24
5 24 120

وبعد الانتهاء من كل اللفات سيكون factorial = 120 وهذا هو المضروب للعدد 5
لاحظ أن القيمة الابتدائية للمتغير factorial كانت 1 لأننا سنقوم بعملية الضرب فيه ولو كانت الابتدائية 0 لكانت كل النواتج ستكون دائمًا بـ 0

ملحوظات مهمة عن الـ for loop

continue و break

في بعض الأحيان قد نحتاج للخروج من الـ loop مبكرًا عندما يحصل شرط معين أو نريد أن نتخطى بعض الحالات ونوقف تنفيذ الـ loop في حالات معينة وأخرى لا

لهذا الغرض يوجد أمرين مهمين في الـ loop هما continue و break

continue

الأمر continue يقوم بتجاهل الكود الذي بعده وينتقل إلى اللفة التالي مباشرة
ويستخدم عندما نريد تجاهل بعض الحالات وننتقل إلى الحالة التالية
ويستخدم في الـ for والـ while والـ do-while

لنقم بمثال بسيط لنقوم بطباعة الأرقام من 0 إلى 4 ولكن نريد تجاهل الرقم 2

for (int i = 0; i < 5; i++) {
    if (i == 2) {
        continue;  // تجاهل هذه اللفة ولا تكمل وانتقل إلى اللفة التالية
    }

    cout << i << '\n';
}

هنا لاحظ أننا قمنا بتعريف متغير i ونريد طباعة الأرقام من 0 إلى 4
لكن قبل طباعة الرقم نقوم بالتحقق إذا كان الرقم 2 نقوم بتجاهله وننتقل إلى الرقم التالي عن طريق continue
وهكذا سيتم طباعة الأرقام 0 و 1 و 3 و 4 فقط ولن يتم طباعة الرقم 2


مثال آخر نريد طباعة الأرقام الزوجية فقط التي تقع بين 0 و 10

for (int i = 0; i <= 10; i++) {
    if (i % 2 != 0) {
        continue;  // انتقل إلى اللفة التالية عندما تقابل أي رقم فردي
    }

    cout << i << '\n';
}

هنا قمنا بتعريف متغير i ونريد طباعة الأرقام من 0 إلى 10
لكن قبل طباعة الرقم نقوم بالتحقق إذا كان الرقم فردي نقوم بتجاهله وننتقل إلى الرقم التالي عن طريق continue

وهكذا سيتم طباعة الأرقام الزوجية فقط 0 و 2 و 4 و 6 و 8 و 10

break

الأمر break يقوم بإيقاف تنفيذ الـ loop فورًا والخروج منه وليس الانتقال إلى اللفة التالية كما في continue

فمثلاً لنفترض أننا نريد الخروج من الـ loop عندما نصل إلى الرقم 5

for (int i = 0; i <= 10; i++) {
    if (i == 5) {
        break;  // انتهي من الـ loop عندما يكون الرقم 5
    }

    cout << i << '\n';
}

هنا قمنا بتعريف متغير i ونريد طباعة الأرقام من 0 إلى 10
لكنه قبل طباعة الرقم يقوم بالتحقق إذا كان الرقم 5 أم لا
فإذا كان الرقم 5 سيتم الخروج من الـ loop فورًا ولن يتم استكمال الـ loop ولا طباعة الرقم 5 ولا الأرقام التي بعده

بالتالي سيتم طباعة الأرقام من 0 إلى 4 فقط وعندما يصل إلى الرقم 5 سيتم الخروج من الـ loop ولن يستكمل باقي اللفات


لنفترض أننا نقوم بالبحث عن أول رقم يقع بين الـ 0 و 100 ويكون يقبل القسمة على 3 و 5 في نفس الوقت
فنحن هنا سنقوم بعمل الـ loop من 0 إلى 100 ثم نتحقق من كل رقم ونرى هل يقبل القسمة على 3 و 5 في نفس الوقت أم لا

for (int i = 0; i <= 100; i++) {
    if (i % 3 == 0 && i % 5 == 0) {
        cout << "The number " << i << " is divisible by 3 and 5\n";
        break; // لقد وجدنا الرقم الذي نريده فلا داعي لاكمال البحث
    }
}

هنا قمنا بكتابة شرط يقوم بالتحقق إذا كان الرقم يقبل القسمة على 3 و 5 في نفس الوقت
وعندما يتحقق الشرط ونجد الرقم الذي نبحث عنه يمكنك استخدام break للخروج من الـ loop فورًا
لأنه لا داعي لاستكمال البحث من بعد ما عثرنا على الرقم الذي نبحث عنه

الـ loops المتداخلة | Nested Loops

الـ loop المتداخلة هي عبارة عن كتابة loop داخل loop آخر
أي أنك تقوم بعمل while داخل while أو for داخل for أو do-while داخل do-while

وهذا يعني أن مع كل لفة في الـ loop الخارجية سيتم تنفيذ كل اللفات في الـ loop الداخلية

لنفترض أننا نريد طباعة جدول الضرب من 1 إلى 10 للأرقام 1 و 2 و 3 فقط

for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 10; j++) {
        cout << i << " × " << j << " = " << i * j << '\n';
    }
}

هنا قمنا بعمل for داخل for
الـ for الخارجية سيتم تنفيذها 3 مرات لأننا قمنا بتحديد الشرط i <= 3
ولأنا نريد بناء جدول الضرب للأرقام 1 و 2 و 3 فقط كما قلنا فالـ for الخارجية ستلف على هذه الأرقام وكل مرة المتغير i سيأخذ قيمة جديدة
ومع كل رقم تمر عليه الـ for الخارجية نحتاج لـ for أخرى تهتم بعملية حساب جدول الضرب من 1 إلى 10 لهذا الرقم
وهذا ما قمنا به في الـ for الداخلية التي تقوم بطباعة جدول الضرب من 1 إلى 10 للرقم الذي تم تحديده في الـ for الخارجية

لنحلل الكود بالتفصيل

أولًا قيمة المتغير i في الـ for الخارجية سيبدأ من 1 ثم ستلف الـ for الداخلية على الأرقام من 1 إلى 10 في المتغير j

قيمة i قيمة j التعويض بالقيم نتيجة الطباعة
1 1 cout << 1 << " × " << 1 << " = " << 1 * 1 1 × 1 = 1
1 2 cout << 1 << " × " << 2 << " = " << 1 * 2 1 × 2 = 2
1 3 cout << 1 << " × " << 3 << " = " << 1 * 3 1 × 3 = 3
1 4 cout << 1 << " × " << 4 << " = " << 1 * 4 1 × 4 = 4
1 5 cout << 1 << " × " << 5 << " = " << 1 * 5 1 × 5 = 5
1 6 cout << 1 << " × " << 6 << " = " << 1 * 6 1 × 6 = 6
1 7 cout << 1 << " × " << 7 << " = " << 1 * 7 1 × 7 = 7
1 8 cout << 1 << " × " << 8 << " = " << 1 * 8 1 × 8 = 8
1 9 cout << 1 << " × " << 9 << " = " << 1 * 9 1 × 9 = 9
1 10 cout << 1 << " × " << 10 << " = " << 1 * 10 1 × 10 = 10

الآن الـ for الداخلية قد انتهت من طباعة جدول الضرب من 1 إلى 10 للرقم 1
وستبدأ الآن الـ for الخارجية في اللفة التالية وسيأخذ المتغير i قيمة 2
وستبدأ الـ for الداخلية مجددًا اللف من 1 إلى 10 في المتغير j

قيمة i قيمة j التعويض بالقيم نتيجة الطباعة
2 1 cout << 2 << " × " << 1 << " = " << 2 * 1 2 × 1 = 2
2 2 cout << 2 << " × " << 2 << " = " << 2 * 2 2 × 2 = 4
2 3 cout << 2 << " × " << 3 << " = " << 2 * 3 2 × 3 = 6
2 4 cout << 2 << " × " << 4 << " = " << 2 * 4 2 × 4 = 8
2 5 cout << 2 << " × " << 5 << " = " << 2 * 5 2 × 5 = 10
2 6 cout << 2 << " × " << 6 << " = " << 2 * 6 2 × 6 = 12
2 7 cout << 2 << " × " << 7 << " = " << 2 * 7 2 × 7 = 14
2 8 cout << 2 << " × " << 8 << " = " << 2 * 8 2 × 8 = 16
2 9 cout << 2 << " × " << 9 << " = " << 2 * 9 2 × 9 = 18
2 10 cout << 2 << " × " << 10 << " = " << 2 * 10 2 × 10 = 20

الآن الـ for الداخلية قد انتهت من طباعة جدول الضرب من 1 إلى 10 للرقم 2
وستبدأ الآن الـ for الخارجية في اللفة التالية وسيأخذ المتغير i قيمة 3
وستبدأ الـ for الداخلية مجددًا اللف من 1 إلى 10 في المتغير j

قيمة i قيمة j التعويض بالقيم نتيجة الطباعة
3 1 cout << 3 << " × " << 1 << " = " << 3 * 1 3 × 1 = 3
3 2 cout << 3 << " × " << 2 << " = " << 3 * 2 3 × 2 = 6
3 3 cout << 3 << " × " << 3 << " = " << 3 * 3 3 × 3 = 9
3 4 cout << 3 << " × " << 4 << " = " << 3 * 4 3 × 4 = 12
3 5 cout << 3 << " × " << 5 << " = " << 3 * 5 3 × 5 = 15
3 6 cout << 3 << " × " << 6 << " = " << 3 * 6 3 × 6 = 18
3 7 cout << 3 << " × " << 7 << " = " << 3 * 7 3 × 7 = 21
3 8 cout << 3 << " × " << 8 << " = " << 3 * 8 3 × 8 = 24
3 9 cout << 3 << " × " << 9 << " = " << 3 * 9 3 × 9 = 27
3 10 cout << 3 << " × " << 10 << " = " << 3 * 10 3 × 10 = 30

الآن الـ for الداخلية قد انتهت من طباعة جدول الضرب من 1 إلى 10 للرقم 3
ثم عندما تحاول الـ for زيادة قيمة المتغير i إلى 4 ستجد أن الشرط لم يعد يتحقق وسيتوقف التنفيذ لأن الشرط كان i <= 3

لاحظ أن الـ for الخارجية تمر على الأرقام 1 و 2 و 3 ومع كل رقم تمر عليه الـ for الداخلية التي تمر على الأرقام من 1 إلى 10
فعندما كانت قيمة الـ i الخاصة بالـ for الخارجية تساوي 1 كانت قيمة الـ j الخاصة بالـ for الداخلية تتغير من 1 إلى 10 وكل مرة يتم طباعة جدول الضرب الخاصة بالرقم i

وهكذا سيتم طباعة جدول الضرب من 1 إلى 10 للأرقام 1 و 2 و 3 فقط


مثال آخر نريد طباعة الأرقام من 1 إلى 5 ولكن نريد طباعة الرقم 1 مرة واحدة والرقم 2 مرتين والرقم 3 ثلاث مرات وهكذا

هل يمكنك تخيل الكود كيف سيكون ؟
فكر قليلًا ثم انظر للشرح

نحن نريد طباعة الأرقام من 1 إلى 5 لذا بديهيًا سنقوم بعمل for يبدأ من 1 وينتهي عند 5
ثم المسألة تقول لنا أن نطبع الرقم 1 مرة واحدة والرقم 2 مرتين والرقم 3 ثلاث مرات وهكذا

لذا مع كل لفة تقوم بها الـ for الخارجية سنقوم بعمل for أخرى داخلية تقوم بطباعة الرقم بعدد تكرار نفسه
فلو كان i الخاص بالـ for الخارجية يساوي 1 سنقوم بعمل for داخلية تبدأ من 1 وتنتهي عند 1
وهذا يعني أن الرقم 1 سيتم طباعته مرة واحدة
فلو كان i الخاص بالـ for الخارجية يساوي 2 سنقوم بعمل for داخلية تبدأ من 1 وتنتهي عند 2
وهذا يعني أن الرقم 2 سيتم طباعته مرتين

وهكذا سيتم طباعة الأرقام من 1 إلى 5 بالتكرار المطلوب

for (int i = 1; i <= 5; i++) {
    for (int j = 1; j <= i; j++) {
        cout << i << '\n';
    }
}

هنا قمنا بعمل for داخل for
الـ for الخارجية سيتم تنفيذها 5 مرات لأننا قمنا بتحديد الشرط i <= 5
ومع كل لفة تقوم بها الـ for الخارجية سيتم تنفيذ الـ for الداخلية بعدد تكرار نفسه لأن الشرط كما تلاحظ j <= i
وهذا معناه أن عندما تكون قيمة i تساوي 1 ستكون قيمة j في الـ for الداخلية بـ 1 وتنتهي عندما تصل j إلى 2 لأن شرط التكرار يقول j <= i

لنحلل الكود بالتفصيل

الناتج النهائي سيكون كالتالي

1
2
2
3
3
3
4
4
4
4
5
5
5
5
5

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

والكود الذي يحتوي على الـ loop متداخلة مع بضها لا يكون أفضل كود وقد يكون هناك وسيلة لحل المسألة وفعل ما تريده بأسلوب مختلفة

مع الخبرة وتعلم الخوارزميات وهياكل البيانات ستتعلم كيف تقوم بعمل واختيار أفضل وسيلة لحل المسألة وتنفيذها بأفضل شكل ممكن

خاتمة

هذا كل شيء عن الـ loops وأريدك أن تعرف أنها من أهم الأدوات في البرمجة وأن ما قمنا بشرحه وقوله عن الـ for والـ while والـ do-while ستجده في كل لغات البرمجة الأخرى

كنت أود أن استكمل الشرح عن الـ for each وما يشبها في لغات البرمجة لكن هذا سيكون في مقالة أخرى إن شاء الله
أما في المقالة التالية سنقوم بحل بعض المسائل البرمجية باستخدام الـ loops لكي تتمكن من فهمها جيدًا
وسندمجها مع الأراي وكيف تستخدم الـ loop للتعامل مع الأراي وحل المسائل والمشكلات البرمجية من خلالهما

أتمنى أن تكون قد استفدت من هذا الشرح وأن تكون قد فهمت كيفية استخدام الـ loops
هو درس قاسي قليلًا ولكنه مهم جدًا وأرجو أن تكون التالية كافية لترسيخ المعلومة في ذهنك عن طريق حل المسائل