اذهب إلى المحتوى

تُصنَّف التَعْليمات البرمجية (statements) بأي لغة برمجة -ومنها الجافا Java- إلى تَعْليمات بسيطة (simple) وأخرى مُركَّبة (compound). تُعدّ التَعْليمات البسيطة -مثل تَعْليمَة الإِسْناد (assignment) وتَعْليمَة اِسْتدعاء البرامج الفرعية (subroutine call)- اللَبِنة الأساسية لأيّ برنامج. في المُقابل، تتكون التَعْليمات المُركَّبة -مثل تَعْليمَة حَلْقة التَكْرار while وتَعْليمَة التَفْرِيع الشَّرطيّة if- من عدد من التَعْليمات البسيطة، وتُعرَف باسم بُنَى التحكُّم (control structures)؛ نظرًا لأنها تَتَحكَّم بترتيب تَّنْفيذ التَعْليمات.

سنتناول بُنَى التحكُّم (control structures) المُدَعَّمة بلغة الجافا Java بشئٍ من التفصيل خلال الأقسام الخمسة التالية، بحيث نبدأ في هذا القسم بتَعْليمتي الحَلْقة while و do..while. سنتَعرَّض، خلال ذلك، لكثير من الأمثلة البرمجية التي تُوظِّف بُنَى التحكُّم تلك، كما أننا وبينما نقوم بذلك، سنُطبِّق التقنيات المُتَّبَعة بتصميم الخوارزميات (algorithms)، والتي قد تَعرَّضنا لها بالفصل السابق.

تَعْليمَة while

تُكتَب تَعْليمَة حَلْقة التَكْرار (loop)‏ while -والتي قد تَعرَّضت لها مُسْبقًا بالقسم ٣.١- بالصياغة التالية:

while ( <boolean-expression> )
    <statement>

لا يُشترَط أن تتكوَّن التعليمة -المُشار إليها بالأعلى، والمعروفة باسم مَتْن حَلْقة التَكْرار (loop body)- من تَعْليمَة واحدة فقط، فبالطبع يُمكنها أن تكون كُتليّة البِنْية (block statement)، بحيث تَتضمَّن عدة تَعْليمَات مُحَاطة بزوج من الأقواس. يَتكرَّر تَّنْفيذ مَتْن الحَلْقة (body of the loop) طالما كان التعبير -المُشار إليه بالأعلى- مُتحقِّقًا. تتكون تلك العبارة من تَعبير منطقي (boolean expression)، وتُعرَف باسم الشَّرْط الاستمراري (continuation condition) لحَلْقة التَكْرار أو باسم اختبار حَلْقة التَكْرار.

نحتاج الآن لإِيضاح عدة نقاط بشيء من التفصيل. أولًا، ماذا يَحدُث إذا لم يَتحقَّق شَّرْط حَلْقة التَكْرار ولو لمرة واحدة على الأقل، بمعنى أنه لم يَكُن متحقِّقًا قبل التَّنْفيذ الأوَّليّ لمَتْن الحَلْقة؟ في هذه الحالة، لا يُنفَّذ مَتْن الحَلْقة (body) نهائيًا، وهو ما يعني أن مَتْن حَلْقة while يُمكِن أن يُنفَّذ أيّ عدد من المرات بما في ذلك الصفر. ثانيًا، ماذا لو كان شَّرْط حَلْقة التَكْرار مُتحقِّقًا، لكن وبينما يُنفَّذ مَتْن الحَلْقة لم يَعُدْ الشَّرْط كذلك؟ هل يَتوقَف تَّنْفيذ الحَلْقة بمجرد حُدُوث ذلك؟ ببساطة لا؛ حيث يَستمِر الحاسوب بتَّنْفيذ مَتْن الحَلْقة بالكامل حتى يَصِل إلى نهايته، وعِندها فقط يَقفز عائدًا إلى بداية حَلْقة التَكْرار لفَحْص شَّرْطها (continuation condition) مُجددًا، ومِنْ ثَمَّ، يَستطيع في تلك اللحظة فقط إِيقاف الحَلْقة وليس قبل ذلك.

لنفْحَص إِحدى المشكلات التي تُحلّ باِستخدَام حَلْقة التَكْرار while: حِساب قيمة متوسط (average) مجموعة من الأعداد الصحيحة الموجبة (positive integers)، والتي يَتم إِدْخالها من قِبَل المُستخدِم. تُحسَب قيمة المتوسط (average) عمومًا بحِساب حاصل مجموع الأعداد ثم قِسمته على عَدَدها. سيَطلب البرنامج من المُستخدِم إِدْخال عدد صحيح (integer) وحيد في المرة، وسيَحتفِظ دومًا بقيمة المجموع الكلي للأعداد الصحيحة المُدْخَلة حتى اللحظة الراهنة، وكذلك عَدَدها، كما سيُبقي هذه القيم مُحْدَثة مع كل عملية إِدْخال.

هاك خوارزمية البرنامج (algorithm) مَكتوبة بأسلوب الشيفرة الوهمية (pseudocode):

Let sum = 0     
Let count = 0  

// طالما لا يوجد المزيد من الأعداد الصحيحة للمعالجة
while there are more integers to process:
    // اقرأ عدد صحيح
    Read an integer 
    // ‫أضف قيمته إلى المتغير sum
    Add it to the sum
    // أزد قيمة العداد
    Count it
// ‫اقسم المجموع sum على count لحساب قيمة المتوسط
Divide sum by count to get the average
// اطبع قيمة المتوسط
Print out the average

لكن كيف سنتحقَّق فعليًا من شَّرْط الحَلْقة بالأعلى "طالما لا يوجد المزيد من الأعداد الصحيحة للمعالجة"؟ أحد الحلول هو إِبلاغ المُستخدِم بأن يُدخِل القيمة صفر بعد أن يَنتهِي من إِدْخال جميع البيانات الفعليَّة. ستَنجح تلك الطريقة نظرًا لأننا نَفترِض أن البيانات الفعليَّة المُدْخَلة لابُدّ وأن تَكون من الأعداد الصحيحة الموجبة، وهو ما يَعني أن الصفر يُعدّ قيمة غيْر صالحة من الأساس. لاحِظ أن الصفر هنا ليس قيمة بحدّ ذاتِها يَنبغي تَضْمِينها مع الأعداد المطلوب حِساب مُتوسطها، وإِنما هي فقط مطلوبة كعلامة للإشارة إلى نهاية البيانات الفعليَّة. يُطلق أحيانًا على قِيَم البيانات المُستخدَمة بطريقة مُشابهة اسم بَيَان البداية/النهاية (sentinel value). سنُعدِّل الآن اختبار حَلْقة التَكْرار while ليُصبِح "طالما العدد الصحيح المُدخل لا يساوي الصفر". ستُواجهنا مشكلة آخرى، عندما يُنفَّذ اختبار حَلْقة التَكْرار (loop) لأول مرة، أي قَبْل التَّنْفيذ الأوَّليّ لمَتْن الحَلْقة، لن يكون هناك عدد صحيح قد قُرأ بَعْد، بل ليس هناك أيّ بيانات قد قُرِأت أساسًا، مما يَعني أنه لا يوجد "عدد صحيح مُدخل". ولهذا، يُصبِح اختبار الحَلْقة "طالما العدد الصحيح المُدخل لا يساوي الصفر" -ضِمْن هذا السياق- غَيْر ذي مَعنى. لذلك يَنبغي القيام بأمر ما قَبل البدء بتَّنْفيذ حَلْقة التَكْرار while للتأكد من صلاحية الاختبار حتى مع أول تَّنْفيذ له. تُعرَف هذه العملية باسم التهيئة المبدئية (priming the loop) لحَلْقة التَكْرار، وفي هذه الحالة بالتحديد، يُمكننا ببساطة تهيئة الحَلْقة عن طريق قراءة أول عدد صحيح مُدْخَل قَبل بدء تَّنْفيذ حَلْقة التَكْرار. هاك الخوارزمية (algorithm) المُعدَّلة:

Let sum = 0
Let count = 0

// اقرأ عدد صحيح
Read an integer

// طالما العدد الصحيح المُدخل لا يساوي الصفر
while the integer is not zero:
    // ‫أضف قيمته إلى المتغير sum
    Add the integer to the sum
    // أزد قيمة العداد
    Count it
    // اقرأ عدد صحيح
    Read an integer
// ‫اقسم المجموع sum على count لحساب قيمة المتوسط
Divide sum by count to get the average
// اطبع قيمة المتوسط
Print out the average

لاحِظ إعادة ترتيب تَعْليمَات مَتْن حَلْقة التَكْرار (loop body)؛ لأنه لمّا أصْبَحت أول محاولة لقراءة عدد صحيح (integer) تَحدُث قَبْل بداية حَلْقة التَكْرار (loop)، كان لابُدّ لمَتْن الحَلْقة من أن يبدأ أولًا بمُعالجة ذلك العدد. ثُمَّ بنهاية المَتْن، سيُحاول الحاسوب قراءة عدد صحيح جديد، ليَقفِز بَعْدها عائدًا لبداية الحَلْقة ليَختبِر العدد الجديد المَقروء للتو. عندما يَقرأ الحاسوب قيمة بَيَان النهاية (sentinel value)، ستَتوقَف الحَلْقة، ومِنْ ثَمَّ لن تُعالَج تلك القيمة، كما أنها لن تُضاف إلى حاصل المجموع sum، أو هذه هى الطريقة التي ينبغي أن تَعمَل بها الخوارزمية على الأقل؛ لأن قيمة بَيَان النهاية ليست جزءً من البيانات الفعليَّة كما ذَكَرنا مُسْبَّقًا.

لا تَعمَل الخوارزمية الأصلية طِبقًا لذلك بطريقة صحيحة -بفَرْض إِمكانية تَّنْفيذها بدون إِجراء التهيئة المبدئية (priming) بطريقة ما-؛ لأنها ستَحسِب حاصل مجموع جميع الأعداد الصحيحة وكذلك عَدَدها (count) بما يتَضمَّن قيمة بَيَان النهاية (sentinel value). لمّا كانت تلك القيمة -المُتفق عليها- تُساوي الصفر، فإن حاصل المجموع سيظِلّ لحسن الحظ صحيحًا، ولكن في المقابل سيكون عَدَدها (count) خطأ بفارق واحد. يُطلَق على الأخطاء المُشابهة اسم الأخطاء بفارق الواحد (off-by-one errors)، وهي واحدة من أكثر الأخطاء شيوعًا. اِتضح أن العَدّ أصعب مما قد يبدو عليه.

سنُحوِّل الآن الخورازمية إلى برنامج كامل، نُلاحِظ أولًا أنه لا يُمكِن اِستخدَام التَعْليمَة average = sum/count;‎ لحساب قيمة المتوسط (average)؛ لأنه لمّا كانت قيمة كُلًا من المُتَغيِّرين sum و count من النوع العددي الصحيح int، فإن حاصل قِسمة الأول على الثاني sum/count ستكون أيضًا من النوع العددي الصحيح int، في حين ينبغي للمتوسط أن يكون عددًا حقيقيًا (real number). لقد واجهنا هذه المشكلة مِن قَبْل، ويَتلخَّص حَلّها بضرورة تَحْوِيل واحد من القيمتين إلى النوع double؛ وذلك لإجبار الحاسوب على حِسَاب قيمة حاصل القِسمة (quotient) كعَدَد حقيقي (real number). يُمكِن القيام بذلك عن طريق إجراء عملية التَحْوِيل بين الأنواع (type-casting) على أحد المُتَغيِّرين على الأقل، بحيث يُحوَّل إلى النوع double عن طريق اِستخدَام ‎(‎‎double)sum‎. ولهذا يَحسِب البرنامج قيمة المتوسط باستخدام التعبير average = ((double)sum) / count;‎. يَتوفَّر حلّ آخر، وهو التَّصْريح (declaration) عن المُتَغيِّر sum بحيث يكون أساسًا من النوع double.

حُلَّت مشكلة آخرى بالبرنامج التالي، وهي أنه في حالة إِدْخال المُستخدِم القيمة صفر كأول قيمة مُدْخَل، فلن تتوفَّر أيّ بيانات للمُعالجة، ولهذا يُمكننا اختبار حُدُوث تلك الحالة بفَحْص ما إذا كانت قيمة المُتَغيِّر count ما تزال مُساوية للصفر حتى بَعْد انتهاء حَلْقة التَكْرار while. قد تبدو هذه المشكلة ثانوية، ولكن لابُدّ أن يُغطي أيّ مبرمج مُنتبِه جميع الحالات المُمكنة.

انظر شيفرة البرنامج بالكامل:

import textio.TextIO;

public class ComputeAverage {

    public static void main(String[] args) {

        int inputNumber; // إحدى القيم المدخلة من قبل المستخدم
        int sum; // حاصل مجموع الأعداد الصحيحة الموجبة
        int count;  // عدد الأعداد الصحيحة
        double average; // قيمة متوسط الأعداد الصحيحة الموجبة

        // هيئ قيمة كلا من متغير المجموع والعداد
        sum = 0;
        count = 0;

        // اقرأ مدخل من المستخدم لمعالجته
        System.out.print("Enter your first positive integer: ");
        inputNumber = TextIO.getlnInt();

        while (inputNumber != 0) {
            // أضف قيمة المتغير للمجموع الحالي
            sum += inputNumber;   
            // أزد قيمة العداد بمقدار الواحد
            count++;              
            System.out.print("Enter your next positive integer, or 0 to end: ");
            inputNumber = TextIO.getlnInt();
        }

        // اعرض النتائج 
        if (count == 0) {
            System.out.println("You didn't enter any data!");
        }
        else {
            average = ((double)sum) / count;
            System.out.println();
            System.out.println("You entered " + count + " positive integers.");
            System.out.printf("Their average is %1.3f.\n", average);
        }

    } // ‫نهاية البرنامج main

} // ‫نهاية الصنف ComputeAverage

تعليمة do..while

تَفحْص تَعْليمَة حَلْقة التَكْرار while الشَّرْط الاستمراري لحَلْقة التَكْرار (continuation condition) ببداية الحَلْقة، ولكن أحيانًا ما يكون من الأنسب فَحْصه بنهاية الحَلْقة لا بدايتها، وهذا في الواقع ما تُوفِّره تَعْليمَة do..while. تَتشابه صياغة (syntax) تَعْليمَة الحَلْقة do..while مع تَعْليمَة while تمامًا باستثناء بعض التَغْيِيرات الطفيفة، حيث تُنْقَل كلمة while مصحوبة مع الشَّرْط إلى نهاية الحَلْقة، في حين تُضاف كلمة do في بداية الحَلْقة بدلًا منها. تُكتَب تَعْليمَة حَلْقة التَكْرار do..while بالصياغة التالية:

do
    <statement>
while ( <boolean-expression> );

إذا كانت التعليمة كُتلَة (block) بذاتها، تُصاغ التَعْليمَة كالتالي:

do {
    <statements>
} while ( <boolean-expression> );

لاحِظ وجود الفاصلة المَنقوطة (semicolon) ; بنهاية التَعْليمَة do..while. تُعدّ هذه الفاصلة جزءً أساسيًا من التَعْليمَة do..while، مثلما تُعدّ الفاصلة بنهاية أي تَعْليمَة أخرى -كالإِسْناد (assignment) أو التَّصْريح (declaration)- جزءً أساسيًا منها. ولهذا سيؤدي حَذْف الفاصلة المنقوطة ;، في هذه الحالة، إلى التَسبُّب بحُدوث خطأ في بناء الجملة (syntax error). ينبغي عمومًا أن تنتهي أيّ تَعْليمَة برمجية -بلغة الجافا Java- إِمّا بفاصلة منقوطة ; أو بقوس مُغلِق {.

عندما يُنفَّذ الحاسوب تَعْليمَة حَلْقة التَكْرار do..while، فإنه يبدأ أولًا بتَّنْفيذ مَتْنها (loop body)-التَعْليمَة أو مجموعة التَعْليمَات بداخل الحَلْقة-، ثم يَحسِب قيمة التعبير المنطقي (boolean expression)، فإذا كانت مُساوية للقيمة المنطقية true، فإنه يَقفِز عَائدًا إلى بداية الحَلْقة ويُعيد تَّنْفيذ مَتْنها، لتستمر العملية بَعْدها بنفس الطريقة. أما إذا كانت قيمة التعبير المنطقي مُساوية للقيمة المنطقية false، فإنه يَتوقَف عن تَّنْفيذ الحَلْقة، وينتقل إلى الجزء التالي من الشيفرة. لاحظ أنه لمّا كان شَّرْط الحَلْقة لا يُفحْص إلا بنهاية الحَلْقة، فإن مَتْن حَلْقة التَكْرار do..while دائمًا ما يُنفَّذ مرة واحدة على الأقل.

على سبيل المثال، اُنظر الشيفرة الوهمية (pseudocode) التالية لبرنامج لعبة. ستجد أن استخدام تَعْليمَة الحَلْقة do..while أكثر مُلائمة في هذه الحالة؛ لأنها ستَضمَن أن المُستخدِم قد لعِبَ مباراة واحدة على الأقل. علاوة على ذلك، فإن شَّرْط حَلْقة التَكْرار بالأسفل لن يكون له أيّ مَعنى في حالة تَّنْفيذه في بداية الحَلْقة.

do {
   // العب مباراة
   Play a Game
   // اسأل المستخدم إذا ما كان يريد اللعب مرة آخرى
   Ask user if he wants to play another game
   // اقرأ رد المستخدم
   Read the user's response
//  أعد التكرار طالما كان رد المستخدم هو نعم
} while ( the user's response is yes );

سنُحوِّل الآن هذه الشيفرة الوهمية إلى لغة الجافا. بدايةً ولكي نتجنب الخَوْض في تفاصيل ليست ذا أهمية، سنفترض أن لدينا صَنْف (class) يَحمِل اسم Checkers. أحد أعضاء (member) هذا الصَنْف هو البرنامج الفرعي الساكن (static subroutine)‏ playGame()‎. يُغلِّف هذا البرنامج الفرعي التفاصيل الخاصة باللعبة، ويُمثِل استدعائه إجراء مباراة دَّامَا (checkers) واحدة مع المُستخدِم. يُمكننا الآن اِستخدَام تَعْليمَة استدعاء البرنامج الفرعي (subroutine call) الساكن Checkers.playGame();‎ كبديل عن سَطْر الشيفرة "العب مباراة".

سنَستخدِم الصنف TextIO لتَلَقِّي رد المُستخدِم على سؤال من النوع نعم أم لا؛ حيث يُوفِّر هذا النوع الدالة‏ (function) ‏TextIO.getlnBoolean()‎، والتي تَسمَح للمُستخدِم بإِدْخال إحدى القيمتين "Yes/No" ضِمْن عدة ردود أُخرى صالحة، بحيث يؤول الرد "Yes" إلى القيمة المنطقية true، بينما يؤول الرد "No" إلى القيمة المنطقية false. سنحتاج الآن إلى مُتَغيِّر لتخزين رد المُستخدِم، والذي سيكون بطبيعة الحال من النوع المنطقي (boolean). يُمكن كتابة الخوارزمية كالتالي:

// ‫تساوي True في حالة رغب المستخدم باللعب مجددًا
boolean wantsToContinue;  
do {
   Checkers.playGame();
   System.out.print("Do you want to play again? ");
   wantsToContinue = TextIO.getlnBoolean();
} while (wantsToContinue == true);

وفقًا لشَّرْط حَلْقة التَكْرار بالأعلى، فإنه عندما تَتَساوى قيمة المُتَغيِّر المنطقي wantsToContinue مع القيمة المنطقية false، سيكون ذلك إشارة (signal) إلى ضرورة تَوقُف حَلْقة التَكْرار. يُطلَق عادةً على المُتَغيِّرات المنطقية (boolean variables) المُستخدَمة بهذه الطريقة اسم راية (flag) أو مُتَغيِّر راية -هي متغيرات تُضبَط (set) بمكان ما بالشيفرة لتُفحَص قيمتها بمكان آخر-.

بالمناسبة، عادةً ما يَسخَر بعض المبرمجين -ربما قد يَصفهم البعض بالمُتَحذلِقين- من شَّرْط حَلْقة التَكْرار while (wantsToContinue == true)‎؛ وذلك لأنه مُكافئ تمامًا للشَّرْط while (wantsToContinue)‎؛ فاختبار ما إذا كان التعبير wantsToContinue == true يُعيد القيمة المنطقية true لا يَختلف نهائيًا عن اختبار ما إذا كان المُتَغيِّر wantsToContinue يَحمِل القيمة true ويُعيدها. بالمثل، -وبفَرْض أن لدينا مُتَغيِّر اسمه flag من النوع المنطقي (boolean variable)- يُكافئ التعبير flag == false، والذي يُعدّ أقل فَجاجة من التعبير السابق، تمامًا التعبير ‎!flag‎، حيث الحرف ! هو عَامِل النفي المنطقي (negation operator). يُفضَّل عمومًا كتابة while (!flag)‎ بدلًا من while (flag == false)‎، وبالمثل، كتابة if (!flag)‎ بدلًا من if (flag == false)‎.

على الرغم من أن اِستخدَام تَعْليمَة حَلْقة التَكْرار do..while أحيانًا ما يكون أكثر ملائمة في بعض المسائل، فعمومًا لا يَجعل وجود نوعين مختلفين من تَعْليمَة الحَلْقة (loops) لغة البرمجة أكثر قوة؛ فبالنهاية، أي مشكلة تُحلّ بإحداهما، ستجد أنه من الممكن حلّها بالآخرى. في الواقع، بفرض أن العبارة تُمثل أي كُتلَة شيفرة، فإن التالي:

do {
    <doSomething>
} while ( <boolean-expression> );

يُكافئ تمامًا:

<doSomething>
while ( <boolean-expression> ) {
    <doSomething>
}

بالمثل:

while ( <boolean-expression> ) {
    <doSomething>
} 

يُكافئ:

if ( <boolean-expression> ) {
   do {
       <doSomething>
   } while ( <boolean-expression> );
}

بدون أي تَغْيِير بمَقصِد البرنامج نهائيًا.

تعليمتي break و continue

تَسمَح لك صيغتي حَلْقتي التَكْرار while و do..while باختبار الشَّرْط الاستمراري (continuation condition) إِمّا ببداية الحَلْقة أو بنهايتها على الترتيب. ولكن أحيانًا سترغب في إجراء عملية اختبار شَّرْط أثناء تَّنْفيذ الحَلْقة، أيّ داخل مَتْن الحَلْقة نفسها، أو حتى قد ترغب بإجراء أكثر من عملية اختبار بأماكن مختلفة داخل نفس الحَلْقة. تُستخدَم تَعْليمَة break بلغة الجافا لإيقاف تَّنْفيذ أيّ حَلْقة تَكْرار (loop)، والخروج منها، وذلك بمجرد استدعائها بأيّ مكان داخل الحَلْقة. تُكتَب كالتالي:

break;

عندما يُنفِّذ الحاسوب تَعْليمَة break داخل حَلْقة (loop)، فإنه سيَقفِز مُباشرةً خارج الحَلْقة، ويُنفِّذ الشيفرة التالية بالبرنامج والموجودة أسفل الحَلْقة. انظر المثال التالي:

while (true) {  // يبدو وكأنها ستنفذ بشكل لا نهائي
    System.out.print("Enter a positive number: ");
    N = TextIO.getlnInt();
    // إما أن تكون قيمة المدخل سليمة أو اقفز خارج الحلقة
    if (N > 0)
        break;
    System.out.println("Your answer must be > 0.");
}
// ‫اكمل هنا بعد break

في المثال بالأعلى، إذا أَدْخَل المُستخدِم عدد أكبر من الصفر، سيتحقَّق الشَّرْط، وستُنفَّذ تَعْليمَة break، ولهذا سيَقفِز الحاسوب مباشرة خارج الحَلْقة إلى ما بَعْدها. في المقابل، إذا لم يتحقَّق الشَّرْط، ستُطبَع السِلسِلة النصية "Your answer must be > 0"، ثم سيَقفِز الحاسوب عَائدًا إلى بداية الحَلْقة ليَقرأ مُدْخَل جديد.

قد يبدو السَطْر الأول من الشيفرة بالأعلى while (true)‎ غَيْر مألوف نوعًا ما، ولكنه في الواقع سليم تمامًا. يُسمَح عمومًا لأيّ تعبير منطقي (boolean-valued expression) بأن يكون شَّرْطًا لحَلْقة التَكْرار، أيّ لابُدّ فقط أن تؤول قيمته النهائية إلى قيمة منطقية؛ ليَفحْصها الحاسوب لمعرفة ما إذا كانت مُساوية للقيمة true أم للقيمة false. تُعدّ القيمة المُجرَّدة (literal)‏ true تعبيرًا منطقيًا، والذي يؤول دائمًا إلى القيمة المنطقية true، ولذلك يُمكِن اِستخدَامها كشَّرْط للحَلْقة. يُستخدَم الشَّرْط while (true)‎ لكتابة حَلْقة لا نهائية (infinite loop) أو حَلْقة يُفْترَض الخروج منها باِستخدَام تَعْليمَة break.

يُسمَح باِستخدَام حَلْقات التَكْرار المُتداخِلة (nested loops)، بمَعنى وجود تَعْليمَة حَلْقة تَكْرار داخل أُخرى، ولذلك لابُدّ لنا من فهم طريقة عَمَل تَعْليمَة break ضِمْن هذا السياق. كقاعدة عامة، تُوقِف تَعْليمَة break حَلْقة التَكْرار الأقرب لها فقط، بمَعنى أنها إذا اُستخدِمت بداخل الحَلْقة الداخلية (inner loop)، فإنها ستُوقِف فقط تلك الحَلْقة، لا الحَلْقة الخارجية (outer loop) التي تَشَمَلها. أما إذا أردت إيقاف تَّنْفيذ الحلقة الخارجية، يُمكِنك اِستخدَام ما يُعرَف باسم تَعْليمَة break المُعنوَنة ‏(labeled break).

تَسمَح تَعْليمَة break المُعنوَنة بتحديد صريح للحَلْقة المطلوب الخروج منها. اِستخدَام هذه التَعْليمَة المُعنوَنة غَيْر شائع، ولذلك سنمر عليها سريعًا. تَعمَل العناوين (labels) كالتالي: يُمكِنك ببساطة عَنوَنة أيّ حَلْقة تَكْرار بوَضْع مُعرَّف (identifier) مَتبوع بنقطتان رأسيتان : بمقدمة الحَلْقة. فمثلًا، يمكن عَنوَنة حَلْقة تَكْرار while باِستخدَام mainloop: while...‎. تستطيع الآن اِستخدَام تَعْليمَة break المُعنوَنة بأيّ مكان داخل هذه الحَلْقة عن طريق اِستخدَام الصيغة break mainloop;‎، وذلك بهدف الخروج من هذه الحَلْقة تحديدًا.

على سبيل المثال، تَفحْص الشيفرة بالأسفل ما إذا كانت السِلسِلتين النصيتين s1 و s2 تحتويان على حرف مُشتَرَك. في حالة تَحقُّق الشَّرْط، ستُسْنَد القيمة المنطقية false إلى مُتَغيِّر الراية nothingInCommon، ثُمَّ تُسْتَدعى تَعْليمَة break المُعنوَنة لإيقاف المعالجة عند تلك النقطة:

boolean nothingInCommon;
// افترض أن السلسلتين لا يشتركان بأي حرف
nothingInCommon = true;  
// متغيرات حلقتي التكرار والتي ستأخذ قيم حروف السلسلتين
int i,j;  

i = 0;
bigloop: while (i < s1.length()) {
    j = 0;
    while (j < s2.length()) {
        // إذا كان هناك حرفًا مشتركا
        if (s1.charAt(i) == s2.charAt(j)) { 
            // ‫اضبط قيمة المتغير إلى القيمة false
            nothingInCommon = false;  
            // اخرج من الحلقتين
            break bigloop;  
        }
        j++;  // Go on to the next char in s2.
    }
    i++;  //Go on to the next char in s1.
}

تُعدّ تَعْليمَة continue مرتبطة نوعًا ما بتَعْليمَة break، لكنها أقل اِستخدَامًا منها. تتَخَطَّى تَعْليمَة continue الجزء المُتبقي من التَكْرار الحالي (current iteration) فقط، أي أنها لا تتَسبَّب بالقفز خارج الحَلْقة تمامًا إلى ما بَعْدها كتَعْليمَة break؛ وإنما تَقفِز عائدة إلى بداية نفس الحَلْقة لتَّنْفيذ التَكْرار التالي (next iteration) -بالطبع إذا كان الشَّرْط الاستمراري (continuation condition) لحَلْقة التَكْرار ما زال مُتحقِّقًا. عندما تُستخدَم تَعْليمَة continue داخل حَلْقة مُتداخِلة (nested loop)، فإنها، مثل تَعْليمَة break، تُجرِي هذه العملية على حَلْقة التَكْرار الأقرب لها فقط، أيّ الحَلْقة الداخلية (inner nested loop). وبالمثل، تتوفَّر تَعْليمَة continue المُعنوَنة ‏(labeled continue)، وذلك لتخصيص حَلْقة التَكْرار المراد إجراء عملية continue عليها.

يمكن اِستخدَام تَعْليمَتي break و continue داخل جميع حَلْقات التَكْرار مثل while، و do..while، و for. سنتحدث عن الأخيرة تفصيليًا بالقسم التالي. يُمكن أيضًا اِستخدَام تَعْليمَة break للخروج من تَعْليمَة switch، وهو ما سنتناوله بالقسم ٣.٦. يُسمَح أيضًا باِستخدَامها داخل تَعْليمَة التَفْرِيع الشَّرْطيّة if إذا كانت تَعْليمَة التَفْرِيع موجودة إِمّا ضِمْن حَلْقة تَكْراريّة أو ضِمْن تَعْليمَة switch، ولكن عندها لا يكون المقصود هو الخروج من تَعْليمَة التَفْرِيع if، وإنما الخروج من التَعْليمَة التي تَشتَملها، أي من تَعْليمَة الحَلْقة أو من تَعْليمَة switch. بالمثل، يُمكِن اِستخدَام تَعْليمَة continue داخل تَعْليمَة if بنفس الطريقة، وتتبِّع نفس القواعد بالأعلى.

ترجمة -بتصرّف- للقسم Section 3: The while and do..while Statements من فصل Chapter 3: Programming in the Small II: Control من كتاب Introduction to Programming Using Java.


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...