تعلّم البرمجة بلغة كوتلن (45): أوامر القفز والعودة Jumps and returns

تعلّم البرمجة بلغة كوتلن (45): أوامر القفز والعودة Jumps and returns
أستمع الى المقال

درسنا واستخدمنا عمليًا في دروس سابقة، عدة طرق للقيام بالعمليات المتكررة في شفرة برنامج كوتلن. وهي حلقتي التكرار while، و do … while، بالإضافة للحلقة الأكثر استخدامًا، الحلقة for.

كل ما فعلناه عند استخدام هذه الحلقات التكرارية حتى الآن، هو تركها تعمل حتى تنتهي من الدوران حول النطاق الذي حددناه لها، أو حتى ينتفي شرطاً ما، أي أن تصبح قيمة الشرط false. 

ولكن، ماذا لو أردنا مزيداً من التحكم المرن في سير وتدفّق شفرة هذه الحلقات، بالتمكن من إيقاف الحلقة والخروج منها، قبل إكمال دورانها، أو حتى تجاهل العنصر في الدورة الحالية ومواصلة الدوران؟ 

حسنًا، هذه بالضبط هي مهمة أوامر القفز والعودة، عبر الكلمات المفتاحية break و continue و return، والتي يمكننا جعلها أكثر مرونة عبر استخدام التسميات Labels. كل ذلك، سنشرحه نظريًا وعمليًا في الفقرات التالية من هذا الدرس.

لمحة تاريخية:

كتب المبرمجون الأوائل الشفرات للمعالج مباشرةً. مستخدمين إما أكواد التشغيل الرقمية (opcodes) كتعليمات، أو لغة التجميع assembly، والتي تُتَرجم أيضًا إلى أكواد التشغيل. يُعد هذا النوع من البرمجة منخفض المستوى جدًا. حيث كان يتم القفز jump إلى أماكن أخرى في شفرة البرنامج بطريقة مباشرة. بعد ذلك، قامت اللغات عالية المستوى الأولى (مثل FORTRAN و ALGOL و Pascal و C و ++C) بتكرار هذه الممارسة من خلال تطبيق كلمة goto المفتاحية.

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

كذلك كوتلن توفر قفزات مقيدة داخل الشفرة، باستخدام break و continue. باستخدام هاتين الكلمتين، لا يمكن القفز إلا إلى آخر حلقة التكرار عبر break، أو القفز إلى بداية الحلقة عبر continue، وليس إلى أي مكان آخر في الشفرة.

عمليًا، يُعد استخدام هاتين الكلمتين قليل في برامج كوتلن. نحن هنا نتعلمهما، لفهم كيفية تطبيقهما في كوتلن، إذا احتجنا استخدامهما. ولكن عمومًا، سنتعلم بدايةً من القسم القادم في هذه الدورة، آليات أخرى أفضل توفّرها كوتلن. 

الكلمة المفتاحية break:

يتم استخدام الكلمة break لإنهاء تنفيذ دوران أقرب حلقة تكرار، عند تحقق شرط أو شروط معينة، غير تلك التي نضعها للحلقة. وتكون في أبسط صورها عند استخدامها مع كتلة if-else:

for (i in 1..10) {  

   // do something

   if (checkCondition){  

       break 

   }  

}

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

في كل دورة للحلقة، ستفحص كتلة if مما إذا كانت قيمة i تساوي 5. إذا كانت نتيجة الشرط false، أي i لا يساوي 5، سيتم تجاهل مابداخل if، وطباعة قيمة المتغير i، ثم مواصلة الدوران. أمّا إذا تحقق شرط if، أي أصبح true، يتم تنفيذ الأمر break، والذي عمله هو إيقاف دوران أقرب حلقة تكرار والخروج منها.

ستكون نتيجة تنفيذ البرنامج أعلاه، هي:

تم ايقاف الحلقة، بعد دورانها ﻷربع مرات فقط. وهذا لأنه ستكون قيمة المتغير i في المرات العشر، هي قيمة عنصر واحد من النطاق الذي وضعناه. ففي الدورة الأولى للحلقة، ستكون قيمة i هي 1، ثم 2 في الدورة الثانية … وهكذا. إلى وصلت إلى أن تكون قيمة المتغير تساوي 5، حينها تحقق شرط if وتم تنفيذ ما بداخلها. ونتيجة لإيقاف عمل الحلقة، لم يتم طباعة قيمة المتغير i للمرة الخامسة. 

نلاحظ أيضًا، أنه تم تنفيذ دالتي الطباعة ()println قبل وبعد الحلقة، هذا لأن break تنهي عمل الحلقة فقط ولا دخل لها بما هو خارجها.

الكلمة المفتاحية continue:

تعمل الكلمة continue، على تخطي الدورة الحالية للحلقة وليس إيقافها بالكامل كما تفعل break. فمثلًا، عند استخدامها مع مثالنا السابق:

هذه هي نفس الشفرة من الفقرة السابقة، فقط استبدلنا الكلمة break بالكلمة continue. ما ستقوم به continue، هو تخطي طباعة قيمة المتغير i حينما تساوي 5 فقط، وتترك الحلقة تواصل دورانها:

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

استخدام break و continue مع حلقات for متداخلة:

عند استخدام أمري break و continue في الحلقات المتداخلة Nested Loop، يؤثّران فقط في الحلقة التي تُحيط بهما، أي الأقرب لهما.

استخدام break:

من المفترض أن تدور كل حلقة منهما 4 مرات حسب شرط النطاق المعطى لهما. لذلك، عند بدء تنفيذ الحلقة الخارجية، سيتم تنفيذ الحلقة داخلها قبل كل شيء آخر، لأنها هي المتواجدة في الأعلى. في كل دورة واحدة للحلقة الخارجية، ستدور الداخلية 4 مرات، ثم ينتهي عملها. هذا يعني، أن الخارجية ستدور 4 مرات، والداخلية 16 مرة.

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

نتيجة لاستخدام break في الحلقة الداخلية، تم إيقاف عملها في كل مرة يكون متغيرها j يساوي 2. امّا الحلقة الخارجية، فقد دارت 4 مرات كما هو مفترض منها، لأن break لم تؤثر عليها.

استخدام continue:

إذا استبدلنا break بـ continue في الشفرة السابقة:

ستتخطى الحلقة الدورة التي يكون فيها قيمة المتغير j يساوي 2 فقط، وتواصل الدورة الثالثة والرابعة:

كما نرى في نتيجة الطباعة، دارت الحلقة لثلاث مرات من أصل أربعة، لأن continue ألغت الدورة الثانية والتي يساوي فيها المتغير 2 == j.

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

التسميات Labels:

التسميات هي أي كلمة تنتهي بالعلامة @. يمكننا استخدام أي كلمة ما عدا الكلمات المفتاحية والمحجوزة في لغة كوتلن. نستخدم التسميات في الحلقات، لإخبار مترجم كوتلن أن أمر break أو continue، يخص الحلقة التي تحمل هذه التسمية، وليس الحلقة التي تتواجد بها:

وضعنا تسمية @outer، للحلقة الخارجية. ثم في الحلقة الداخلية، وضعنا نفس التسمية بعد الكلمة break. الآن حين وصول قيمة j إلى 2، سيتم ايقاف الحلقة الخارجية، وتنفيذ الحلقة الداخلية لمرة واحدة فقط، وهي المرة التي تكون فيها قيمة المتغير j تساوي 1:

أمّا عند استخدام continue:

يتم تنفيذ الحلقة الداخلية 4 مرات، وهي عدد لفّات الحلقة الخارجية:

وبالرغم من أن الحلقة الخارجية دارت لـ 4 مرات، ولكن لم يتم تنفيذ دالة الطباعة التي تتواجد بداخلها، نتيجة لأن continue تخطّت تنفيذها في كل مرّة من المرات الأربع.

استخدام break و continue مع when:

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

في الشفرة أعلاه، سيتم طباعة قيمة المتغير i في كل دورة للحلقة، إلّا حينما تكون قيمة i تساوي 3 سيتم التخطي عبر continue. أمّا حينما تكون قيمة i تساوي 6، سيتم ايقاف الحلقة بالكامل:

الكلمة المفتاحية return:

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

فإذا أعدنا استخدام نفس الشفرة من فقرة الكلمة المفتاحية break، مع استبدال الكلمة break بالكلمة return:

عند تحقق الشرط في كتلة if، أي عندما يكون المتغير i يساوي 5، سيتم تنفيذ التعبير return. حينها لن يتم إنهاء عمل حلقة for فحسب، بل إنهاء الدالة ()main كلها، ولن يتم تنفيذ أي شيء يأتي بعد return. وهذا يعني أنه لن يتم طباعة الجملة النّصية: “بعد إنتهاء عمل الحلقة” الموجودة في آخر دالة ()main، بالرغم من أنها خارج الحلقة for:

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

ملحوظة: استخدمنا في هذا الدرس الحلقة for فقط. ولكن كل ذلك ينطبق أيضًا على حلقة التكرار while.

الخلاصة:

تعبيرَي break و continue يُعدّان قفزات مقيدة أكثر سهولة إلى حدِّ ما من “goto”. ولكن لتسببهما في إعاقة تدفق وسير شفرة البرنامج، يصبح من الصعب أحيانًا فهم الشفرة التي يكثر استخدامهما بها، لأنها في الغالب ستكون معقّدة وغير قابلة للصيانة. في بعض الحالات، يكون من الأفضل كتابة دوال واستخدام return، بدلًا عن هذين التعبيرين. وهو ما سنتعلمه تفصيليًا في القسم التالي، البرمجة الوظيفية.

هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.