تعلم كيفية البرمجة ب #C


الدرس: الحلقات التكرارية


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

للحصول على حلقة


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

int compteur;
for (compteur = 0; compteur < 50; compteur++)
{
    Console.WriteLine("Bonjour C#");
} 
يعطي :
c sharp .net framework
هنا نحدد عددًا صحيحًا "عدادًا" . تتم تهيئة إلى 0 في بداية الحلقة (عداد = 0). بعد كل تنفيذ للكود الخاص بـ for، أي في كل تكرار ، سيعرض "Bonjour C#" .
في نهاية كل تكرار ، يتم زيادة متغير العداد (عداد ++) ونكرر العملية طالما كانت الحالة "عداد <50 " صحيحة.
بالطبع ، compteur يمكن الوصول إلى المتغير في الحلقة ويقوم بتغيير القيمة مع كل تكرار.

int compteur;
for (compteur = 0; compteur < 50; compteur++)
{
    Console.WriteLine("Bonjour C# " + compteur);
} 
يعطي :
c sharp .net framework
سيعرض هذا الكود السابق قيمة المتغير بعد كل تكرار ، وبالتالي من 0 إلى 49. في الواقع ، عندما يذهب العداد إلى 50 ، فإن الشرط لم يعد صحيحًا ونحن نذهب إلى الإرشادات التالية.
بشكل عام ، نستخدم الحلقة for لاجتياز صفيف. وبالتالي ، يمكننا استخدام العداد كفهرس للوصول إلى عناصر المصفوفة:

string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
int indice;
for (indice = 0; indice < 7; indice++)
{
    Console.WriteLine(jours[indice]);
} 
في هذه الحلقة ، كما هو الحال في مؤشر التكرار الأول بقيمة 0 ، سنقوم بعرض عنصر المصفوفة في الموضع 0 ، وهو "الاثنين".
في التكرار التالي ، يتغير الفهرس إلى 1 ، نعرض عنصر المصفوفة في الموضع 1 ، أي "الثلاثاء" ، وهكذا.
يعطي :
c sharp .net framework
كن حذرًا من أن الفهرس لا يتجاوز حجم المصفوفة ، وإلا فإن الوصول إلى فهرس خارج حدود المصفوفة سيؤدي إلى حدوث خطأ في وقت التشغيل. لتجنب ذلك ، نستخدم عمومًا حجم المصفوفة كشرط نهائي:

string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
int indice;
for (indice = 0; indice < jours.Length; indice++)
{
    Console.WriteLine(jours[indice]);
} 
هنا jours.Length تُرجع حجم الصفيف ، وهي 7.
من الشائع للغاية تنفيذ حلقة من خلال تمرير كل العناصر واحدة تلو الأخرى ، لكن من الممكن تغيير شروط البداية وظروف النهاية والعنصر الذي يؤثر على شرط النهاية.
لذلك المثال التالي:

int[] chiffres = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (int i = 9; i > 0; i -= 2)
{
    Console.WriteLine(chiffres[i]);
} 
دعنا نذهب من خلال الجدول 2 في 2 ، ابتداء من النهاية.
مما يعطينا:
c sharp .net framework
يمكنك أن ترى أنه في هذا المثال ، قمنا بتعريف الأعداد الصحيحة iمباشرة في التعليمات for، إنها وسيلة تسمح لنا بوجود المتغير i الموجود فقط داخل الحلقة ، لأنه نطاق يتوافق مع الأقواس التي تحدد for .
كن حذرا ، فمن الممكن أن نفعل أي شيء وكل ما نريد في هذه الحلقات. كما يمكن أن يحدث أن ينتهي بنا المطاف مع الأخطاء ، مثل الحلقات اللانهائية.
على سبيل المثال ، الكود التالي:

for (int indice = 0; indice < 7; indice--)
{
    Console.WriteLine("Test" + indice);
} 
هي حلقة لانهائية. في الواقع ، نقوم بتعديل متغير العدّاد من خلال تقليله. إلا أن شرط الخروج من الحلقة صالح لعدّاد يتجاوز أو يساوي القيمة 7 ، وهو ما لن يحدث أبدًا.
إذا قمت بتنفيذ التطبيق باستخدام هذا الكود ، فستعرض وحدة التحكم كلمة "Test" مع فهرسها إلى أجل غير مسمى. الحل الوحيد للخروج من البرنامج هو إغلاق التطبيق.
الحل الآخر هو الانتظار حتى ينتهي البرنامج ...
ولكن هل قلت للتو أن الحلقة كانت بلا حدود؟
نعم هذا صحيح ، لكن في الحقيقة ، نحن نواجه حالة الحدود لـ #C . هذا بسبب متغير الفهرس. الفهرس هو عدد صحيح نخفضه. في البداية ، يساوي الصفر ، ثم -1 ، ثم -2 ، وما إلى ذلك.
عندما يصل متغير المؤشر إلى الحد الأدنى الذي يمكن لنوع int إدارته ، أي -2147483648 نحن نسميه فائض سِعة الذاكرة المخصصة للاعداد الصحيحة  . دون الخوض في التفاصيل ، لا يمكن تخزين عدد صحيح أصغر وبالتالي فإنه يتم الحلقات ويبدأ من جديد في عدد صحيح أكبر ، أي 2147483647.
لتلخيص ، الفهرس:
  • 0
  • -1
  • -2
  • ...
  • -2147483647
  • -2147483648
  • 2147483647
وهكذا ، وجدت أعلى من 7 ، تنتهي الحلقة.
c sharp .net framework
لذلك من الناحية الفنية ، ليست حلقة لا نهائية ، ولكن مهلا ، لقد انتظرنا طويلاً للوصول إلى حالة الحدود هذه تمامًا.
قبل كل شيء ، صادفنا حالة غير متوقعة. هنا ينتهي "بشكل جيد" ، ولكن يمكن أن ينتهي في تعطل التطبيق.
في جميع الحالات ، من الضروري للغاية التحكم في ظروف الخروج من الحلقة لتجنب الحلقة اللانهائية ، أحد كوابيس المطور!

حلقة Foreach


نظرًا لأنه من الشائع جدًا استخدام الحلقات لتصفح صفيف أو قائمة ، فإن #C لديها عامل تشغيل محدد : foreach يمكن ترجمته بواسطة "لكل": لكل عنصر من عناصر المصفوفة ...

string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
foreach (string jour in jours)
{
    Console.WriteLine(jour);
} 
ستتيح لنا هذه الحلقة استعراض جميع عناصر المصفوفة "jours". في كل تكرار ، ستنشئ الحلقة سلسلة أحرف "jour" تحتوي على العنصر الحالي للصفيف. لاحظ أن المتغير "jour" سيكون له نطاق مساوٍ للكتلة foreach . يمكننا بالتالي استخدام هذه القيمة في نص الحلقة ولماذا لا نعرضها كما في المثال السابق.
والتي سوف تنتج:
c sharp .net framework
تعمل التعليمات foreach أيضًا مع القوائم ، على سبيل المثال التعليمة البرمجية التالية:

List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
foreach (string jour in jours)
{
    Console.WriteLine(jour);
} 
سيتيح لنا عرض جميع أيام الأسبوع الواردة في قائمة الأيام.
يرجى ملاحظة أن الحلقة  foreach هي حلقة للقراءة فقط . هذا يعني أنه لا يمكن تعديل عنصر التكرار الحالي.
على سبيل المثال ، الكود التالي:

List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
foreach (string jour in jours)
{
    jour = "pas de jour ";
} 
سيؤدي خطأ التحويل البرمجي التالي:
اقتباس: مترجم
لا يمكن تعيين "jour" لأنه "متغير التكرار foreach "
لاحظ أيضًا أن فريق ترجمة Visual Studio Express قد أحرز بعض التقدم ...
إذا كنا نرغب في استخدام حلقة لتغيير قيمة قائمتنا أو صفيفنا ، فسيتعين علينا المرور عبر حلقة for :

List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" } ;
for (int i = 0; i < jours.Count; i++ )
{
    jours[i] = "pas de jour !";
} 
سيحدث لك أيضًا يوم واحد (يحدث لجميع المطورين) أن ترغب في تعديل قائمة في حد ذاتها أثناء حلقة foreach . هذا يعني إضافة عنصر إليه ، وحذفه ، إلخ ...
كما أنه من المستحيل لأنه منذ اللحظة التي تدخل فيها الحلقة foreach، تصبح القائمة غير قابلة للتعديل.
خذ مثالًا كلاسيكيًا لإيجاد قيمة في قائمة لحذفها.  للقيام بما يلي:

List>string< jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
foreach (string jour in jours)
{
    if (jour == "Mercredi")
        jours.Remove(jour);
} 
والتي قد تبدو صحيحة تمامًا ، بالإضافة إلى أنها لا تسبب خطأً في الترجمة.
إلا أنه إذا قمت بتشغيل التطبيق ، فسوف تحصل على الخطأ التالي:
c sharp .net framework
الذي يعطل تطبيقنا. يخبرنا البرنامج أن "المجموعة قد تم تعديلها" وأنه "قد لا يتم تنفيذ عملية التعداد" .
لذلك من المستحيل القيام بحذفنا أيضًا.
كيف يمكنك أن تفعل؟
حسنًا ، توجد عدة حلول.  الذي يتبادر إلى الذهن أولاً هو استخدام حلقة for على سبيل المثال:

for (int i = 0 ; i < jours.Count ; i++)
{
    if (jours[i]== "Mercredi")
        jours.Remove(jours[i]);
} 
هذا الحل مثير للاهتمام هنا ، لكنه قد يشكل مشكلة في المواقف الأخرى. في الواقع ، نظرًا لأننا أزلنا عنصرًا من القائمة ، فسوف ينتهي بنا المطاف بتناقض بين الفهرس الحالي والعنصر الذي نحاول الوصول إليه. في الواقع ، عندما يكون اليوم الحالي هو الأربعاء ، فإن المؤشر i يساوي 2. إذا أزلنا هذا العنصر ، سينتهي الخميس في الموضع 2. وسنفتقد تحليله لأن الحلقة سوف تستمر في "فهرس 3 ، والذي سيكون يوم الجمعة.

يمكننا تجنب هذه المشكلة عن طريق تشغيل الحلقة للخلف:

for (int i = jours.Count - 1; i >= 0; i--)
{
    if (jours[i] == "Mercredi")
        jours.Remove(jours[i]);
} 
لاحظ أنه بشكل عام ، لا ينبغي لنا تعديل المجموعات التي نتصفحها حاليًا.
لن ندرس الحلول الأخرى لأنها تستدعي المفاهيم التي سنرىها بالتفصيل في دورة مستقبلية.
بعد قراءة هذا ، قد يكون لديك انطباع بأن الحلقة foreach ليست مرنة وصعبة الاستخدام ، كما أنك قد تستخدم شيئًا آخر ...
سترى عند استخدامها أنها غير عملية ، إنها في الواقع عملية للغاية. عليك فقط معرفة حدودك.
لقد رأينا أن التعليمات foreach قد مكّنت من تنفيذ كافة عناصر صفيف أو قائمة. في الواقع ، من الممكن تصفح جميع الأنواع التي يمكن حصرها .
سنرى في الدرس التالي ما الذي يميز نوعًا لا حصر له ، لأنه في الوقت الحالي ، إنه سر! صه .

الحلقة while


عادة ما تستخدم الحلقة While أقل من for أو foreach . إنها الحلقة التي تسمح لنا بعمل شيء ما لم يتم التحقق من الحالة. يشبه إلى حد كبير الحلقة for، لكن الحلقة for متخصصة في تشغيل الجدول بينما تكون الحلقة while أكثر عمومية.
مثلا :

int i = 0;
while (i < 50)
{
    Console.WriteLine("Bonjour C#");
    i++;
} 
لنكتب "Bonjour C#" 50 مرة على التوالي.
هنا ، while يتم تقييم حالة في بداية الحلقة.
في المثال التالي:

int i = 0;
do
{
    Console.WriteLine("Bonjour C#");
    i++;
}
while (i < 50); 
يتم تقييم حالة خروج الحلقة في نهاية الحلقة. يتيح استخدام الكلمة الأساسية do الإشارة إلى بداية الحلقة.
يعني هذا بشكل ملموس أنه في المثال الأول ، لا يمكن أبدًا تنفيذ الكود الموجود داخل الحلقة ، إذا تم على سبيل المثال  تهيئة  الرقم الصحيح i إلى 50. على العكس ، سنمر مرة واحدة على الأقل في نص حلقة المثال الثاني ، حتى إذا  تمت تهيئة العدد الصحيح i إلى 50 لأنه يتم تقييم شرط الخروج من الحلقة في النهاية.
لا تتعلق شروط ترك الحلقات بالضرورة بوجود عداد أو فهرس ، ويمكن تقييم أي تعبير. مثلا :

string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
int i = 0;
bool trouve = false;
while (!trouve)
{
    string valeur = jours[i];
    if (valeur == "Jeudi")
    {
        trouve = true;
    }
    else
    {
        i++;
    }
}
Console.WriteLine("Trouvé à l'indice " + i); 
سيقوم الكود السابق بتكرار الإرشادات الموجودة في الحلقة while طالما أن القيمة المنطقية "trouve" خاطئة (بمعنى ، سنتوقف بمجرد أن تكون قيمة trouve صحيحة ). نحن نحلل القيمة في الفهرس i، إذا كانت القيمة هي القيمة المطلوبة ، فنحن نحول القيمة المنطقية إلى true ويمكننا الخروج من الحلقة. خلاف ذلك ، نحن نزيد المؤشر للانتقال إلى التالي.

انتبه أيضًا إلى قيم الخروج من الحلقة. إذا لم نعثر على السلسلة المطلوبة ،  فستستمر i بالزيادة. لن تكون قيمة trouve مساوية لـ True على الإطلاق ، سنظل في الحلقة ونخاطر في الوصول إلى فهرس غير موجود في الجدول.

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

string[] jours = new string[] { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
int i = 0;
bool trouve = false;
while (i < jours.Length && !trouve)
{
    string valeur = jours[i];
    if (valeur == "Jeudi")
    {
        trouve = true;
    }
    else
    {
        i++;
    }
}
if (!trouve)
    Console.WriteLine("Valeur non trouvée");
else
    Console.WriteLine("Trouvé à l'indice " + i); 
وبالتالي ، إذا كان المؤشر أكبر من حجم المصفوفة ، فنحن نخرج من الحلقة ونقضي على خطر حدوث حلقة لا نهائية.
خطأ كلاسيكي هو أن الشرط لا يصبح صحيحًا بسبب خطأ في البرمجة. على سبيل المثال ، إذا نسيت زيادة المتغير i، فسأقوم دائمًا بتحليل القيمة الأولى للصفيف في كل مسار من الحلقة ولن تصل أبداً إلى الحد الأقصى لحجم المصفوفة ، وهي حالة تسمح لي بالخروج من الحلقة .
لا يمكنني تكرار ذلك بما فيه الكفاية: إيلاء اهتمام وثيق لظروف ترك حلقة.

التعليمات break و continue


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

int i = 0;
while (true)
{
    if (i >= 50)
    {
        break;
    }
    Console.WriteLine("Bonjour C#");
    i++;
} 
يمكن أن ينتج عن الكود السابق حلقة لا نهائية. في الواقع ، شرط الخروج من while صحيح دائما. لكن استخدام الكلمة الرئيسية break سيسمح لنا بالخروج من الحلقة بمجرد وصول i إلى القيمة 50. من
المسلم به هنا ، أنه يكفي أن يكون شرط الخروج يتعلق بتقييم العدد الصحيح i . ولكن قد تكون هناك حالات قد يكون من الحكمة فيها استخدام break (خاصة أثناء التنقل!) .
هذا هو الحال في المثال التالي. لنفترض أننا نريد التحقق من وجود قيمة في القائمة. للعثور عليه ، يمكنك تصفح عناصر القائمة وبمجرد العثور عليها ، يمكنك التوقف. في الواقع ، سيكون من غير المجدي الاستمرار في تصفح بقية العناصر:

List<string> jours = new List<string> { "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche" };
bool trouve = false;
foreach (string jour in jours)
{
    if (jour == "Jeudi")
    {
        trouve = true;
        break;
    }
} 
نحن ننقذ أنفسنا هنا من خلال تحليل آخر 3 عناصر من القائمة.
من الممكن أيضًا الانتقال إلى التكرار التالي للحلقة من خلال استخدام الكلمة الأساسية continue . انظر في المثال التالي:

for (int i = 0; i < 20; i++)
{
    if (i % 2 == 0)
    {
        continue;
    }
    Console.WriteLine(i);
} 
هنا ، يسمى عامل التشغيل ٪modulo " . ويوفر باقي القسمة. العملية i % 2 تُرجع 0 اذا كانت i عدد زوجي. لذلك ، بمجرد العثور على رقم زوجي ، ننتقل إلى التكرار التالي بفضل الكلمة الرئيسية continue . هذا يعني أننا لن نعرض سوى الأرقام الفردية.
يعطي :
c sharp .net framework
بالطبع ، كان من الممكن عكس حالة if القيام بذلك Console.WriteLine() فقط في الحالة التي يكون فيها
i % 2 != 0  الأمر متروك لك لاختيار الكتابة التي تفضلها وفقًا للحالات التي ستواجهها.

في الخلاصة


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