تعلّم البرمجة بلغة كوتلن (19): حلقة التكرار for والنطاقات

تعلّم البرمجة بلغة كوتلن (19): حلقة التكرار for والنطاقات
أستمع الى المقال

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

النطاقات Ranges:

في برنامج المتجر الذي شرحناه في درس القيم المنطقية، كان لدينا الدالة التالية:

إذا نظرنا إلى التعبير التالي:

hour >= openTime && hour <= closeTime

سنجد أن هناك نطاق يُمثل الوقت. ما بين الساعة 9 والتي هي قيمة المتغير openTime، والذي يُمثل الحد الأصغر في النطاق. والساعة 22 التي هي قيمة المتغير closeTime، والذي يُمثل الحد الأكبر في النطاق. يجب أن تكون قيمة المتغير hour تساوي أو ضمن حدي هذا النطاق، لينتج التعبير القيمة true.

في كوتلن، يمكننا كتابة التعبير السابق بطريقة أكثر قابلية للقراءة، كالتالي:

hour in openTime..closeTime

استخدمنا الكلمة المفتاحية in والنقطتين (..)، لمعرفة ما إذا كانت قيمة hour تساوي أو تقع ضمن حدي النطاق.

استخدام النقطتين (..) في النطاق:

يعبر معامل النقطتين في المثال أعلاه، عن كلمة (حتى). فمثلًا، إذا أردنا معرفة ما إذا كان الرقم 10 يقع ضمن نطاق عدد صحيح Int، يكون الحد الأول فيه الرقم 1، ويكون الحد الثاني فيه العدد 10، يمكننا فعل التالي:

10 in 1..10

ستكون نتيجة التعبير true، لأنه عند استخدام معامل النقطتين (..)، يتم ضم الحد الأخير في المقارنة، وسيتم مقارنة القيمة 10 مع كل الأرقام في النطاق بما فيها الرقم الأخير 10. خلاف معامل until، والذي سنشرحه في الفقرة التالية.

استخدام المعامل until في النطاق:

عند استخدام المعامل until في النطاق: مع التعبير السابق كالتالي:

10 in 1 until 10

ستكون نتيجة التعبير false في النطاق:، لأن until في النطاق: يستبعد الحد الأخير من المقارنة. بمعنى أنه لن يتم مقارنة القيمة 10 المراد البحث عنها، مع الحد الأخير. لذا، سيتم اعتبارها غير موجودة ضمن النطاق. وهذا الفرق الرئيسي، بين استخدام النقطتين (..)، واستخدام until في النطاقات.

استخدام المعامل downTo في النطاق:

هذا المعامل عكس معامل النقطتين (..) يقوم بترتيب العناصر تنازليًا، أي من الحد الأكبر إلى الأصغر.

استخدام المعامل step في النطاق:

المتتالية الحسابية في الرياضيات Arithmetic progression،‏ هي متتالية من الأعداد حيث يكون الفرق بين أي حدين متتاليين ثابتًا. مثل: 3،2،1…10، الفرق بين الرقم والذي يليه هو 1. أو مثل: 9،7،5،3،1، الفرق بين الرقم والذي يليه هو 2، وهكذا.

في كوتلن لدينا شيء مشابه لهذا، تُمثله الأنواع IntProgression، و LongProgression، و CharProgression. فإذا أردنا طباعة الأرقام في مجموعة معينة وتخطي بعضها بطريقة المتتالية الحسابية، نستخدم المعامل step.

فمثلًا، إذا كان لدينا هذا التعبير باستخدام downTo:

9 downTo 1 step 2

تكون الأرقام التي يشملها النطاق اعلاه، هي: 9،7،5،3،1 مرتبة من اليسار بطريقة تنازلية.

أما باستخدام until:

1 until 9 step 2

تكون الأرقام التي يشملها النطاق اعلاه، هي: 1،3،5،7 مرتبة من اليسار بطريقة تصاعدية. ونلاحظ لا يشمل النطاق الرقم 9، هذا ﻷننا استخدمنا until.

أما باستخدام النقطتين (..):

1..9 step 2

تكون الأرقام التي يشملها النطاق اعلاه، هي: 1،3،5،7،9 مرتبة من اليسار بطريقة تصاعدية.

استخدام النطاقات الحروف Characters:

بجانب الأعداد الصحيحة Int، يمكننا استخدام النطاقات في أنواع بيانات أخرى. مثل: الحروف Char، السلاسل النصية String (حسب الترتيب الأبجدي للغة الإنجليزية).

فمثلًا في الحروف، إذا أردنا أن نعرف هل أن الحرف d يقع ضمن النطاق حده الأول الحرف b وحده الأخير الحرف h، يمكننا فعل التالي:

ستكون نتيجة الطباعة true، ﻷن الحرف d موجود فعليًا ضمن الحروف من a حتى h، حسب الترتيب الأبجدي للغة الإنجليزية. 

نلاحظ أيضًا، أننا وضعنا التعبير مباشرة داخل دالة ()println. هذا ﻷنه تعبير expression، وكما قلنا في درس كتلة التعبير الشرطي if، أن التعبير دائمًا ما يعود بقيمة. هذه القيمة العائدة، هي ما ستطبعه دالة ()println.

سنفهم النطاقات واستخداماتها بطريقة أوضح، في فقرات الحلقة for أدناه.

حلقة for:

تستخدم حلقة for، للدوران داخل مجموعة معينة من العناصر، عنصرًا بعد عنصر. هذه المجموعة من العناصر، من الممكن أن تكون نطاق أعداد صحيحة Integer Range، أو سلسلة نصية String، أو مصفوفات Arrays، أو تجميعات Collections، والتي سنتحدث عنها لاحقًا في هذه السلسلة، وغيرها.

يكون أبسط شكل لحلقة الـ for، كالتالي:

تبدأ الحلقة بالكلمة المفتاحية for، ثم قوسين ( ) نضع بداخلهما، متغير يمكن أن نسميه كما نحب، يُمثل عنصر واحد، ثم الكلمة المفتاحية in، وبعدها مجموعة القيم values التي تحوي العناصر. ونضع ما بين القوسين المعقوفين { }، الشفرة المراد تنفيذها.

 في أول مرة تبدأ فيها حلقة الـ for عملها، ستكون قيمة المتغير element هي قيمة أول عنصر في مجموعة القيم values. ثم يدخل إلى الأقواس المعقوفة وينفذ الشفرة، ثم يعود إلى الأعلى وينظر هل هناك عنصر ثاني في values؟ إذا كان يوجد، سيُمثل المتغير element قيمة العنصر الثاني، ثم يدخل إلى الأقواس المعقوفة وينفذ الشفرة. وهكذا يتكرر الأمر حتى يتم الدوران على كامل عناصر values. لتنهي الـ for عملها بعد آخر عنصر في values.

طباعة قيم نطاقات الأعداد الصحيحة Integer Ranges:

قبل أن نستخدم النطاقات مع حلقة for، دعونا ننظر كيف ستتعامل كوتلن مع نطاقات تم إسنادها إلى متغيرات، كالتالي:

عند تنفيذ البرنامج، سنجد أنه تم طباعة قيم المتغيرات كالتالي:

1..9

1..9

9 downTo 1 step 1

نلاحظ أنه تم طباعة قيمة المتغير range1 كما هي. والمتغير range2 تم طباعة النطاق الذي يُمثله واستبعاد الرقم 10. أما المتغير range3، تم طباعة كلمة اضافية لم نضعها له وهي step. أي أن المتغير range3 يُمثل الأرقام من 9 حتى الرقم 1 ويكون الفرق بين كل رقمين 1.

هذا حدث، ﻷن هذه المتغيرات ليست من النوع العدد الصحيح Int. بل نطاق من العدد كذا حتى العدد كذا. وستستنتج كوتلن على أن المتغيران الأول والثاني، يُمثلان نطاق عددي صحيح IntRange، كل نطاق به مجموعة من الأعداد الصحيحة Int. لذا نوع بياناتهما هو IntRange. أما المتغير الأخير، فهو من النوع IntProgression، بمعنى أنه يطبق مفهوم المتتالية الحسابية في الرياضيات، في تجميعة الأعداد الصحيحة التي يُمثلها.

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

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

حلقة for ونطاقات الأعداد الصحيحة Integer Ranges:

بما أن المتغير range1 هو تجميعة للأعداد التي تتواجد في النطاق من العدد 1 حتى العدد 9، يمكننا الوصول إلى عناصره وطباعتها كالتالي:

في داخل أقواس الحلقة for، أعلنا عن متغير اخترنا له الحرف (i) كاسم. هذا المتغير (i)، هو من سيمثل عناصر النطاق range1 في كل دورة تقوم بها الحلقة. ففي المرة الأولى سيُمثل الرقم 1، ثم يدخل إلى الأقواس المعقوفة ويطبع قيمة المتغير (i). في الدورة الثانية للحلقة، سيُمثل المتغير (i) الرقم الثاني في النطاق وهو الرقم 2، ثم يدخل إلى الأقواس المعقوفة ويطبع قيمة المتغير (i). وهكذا، حتى يطبع الرقم 9، عندها ينتهي عمل الحلقة.

عند انتهاء عمل الحلقة، سينتهي عمل المتغير (i) معها. فهو متغير داخلي خاص بالحلقة فقط ولا يمكن استخدامه أو الإشارة إليه في أي مكان آخر في البرنامج. وبالطبع نوع المتغير هو Int. يمكننا وضع نوعه صراحة هكذا: (Int :)، ولكن ليس هناك داعي لأن كوتلن ستستنتج نوعه على كل حال من نوع العناصر التي في النطاق يمين الكلمة المفتاحية in.

نلاحظ أيضًا، أننا استخدمنا دالة الطباعة التي لا تطبع سطر جديد ()print في هذه الشفرة. هذا لأننا نريد أن يتم طباعة الأرقام بطريقة أفقية وليس بطريقة عمودية كل رقم في سطر لوحده، كما تفعل دالة ()println. ووضعنا مسافة بعد المتغير (i) باستخدام طريقة القوالب النصية في كوتلن، لطباعة مسافة بعد كل طباعة لقيمة المتغير، لفصل الأرقام عن بعضها. كما يظهر في الصورة التالية:

حلقة for ونطاقات الحروف Char:

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

سيتم طباعة الحروف الإنجليزية من الحرف a حتى الحرف الأخير z.

حلقة for والسلاسل النصية Strings:

كما شرحنا في درس أنواع البيانات، السلاسل النصية String، هي عبارة عن مجموعة من المحارف موضوعة بين علامتي تنصيص (” “). مجموعة المحارف هذه، تمثل عناصر السلسلة النصية. يمكننا استخدام حلقة for للوصول لعناصر السلسلة النصية لطباعتها أو فعل أي شيء آخر بها، كالتالي:

ستدور حلقة for على كل عناصر قيمة المتغير characters، وتطبعها حرف حرف بطريقة عمودية، ﻷننا استخدمنا دالة الطباعة ()println.

تخزين الحروف كأرقام في الحاسب:

كل المحارف (رموز وحروف)، يتم تخزينها كأرقام في الحاسب حسب معايير نظام الترميز آسكي ASCII للحروف في الأبجدية اللاتينية، ومنها اللغة الإنجليزية. لهذا السبب، يمكننا إجراء عمليات حسابية بين الأحرف من النوع Char، أو المحارف التي تكون ضمن سلسلة نصية String، والأرقام من نوع Int.

فمثلًا، الرقم 97 يُمثل الحرف الإنجليزي الصغير a، والرقم 122 يُمثل الحرف الإنجليزي الصغير z. والفرق بين الرقمين، هو: 25. إذًا، عند إضافة 25 إلى الحرف a، سينتج لنا الحرف z. وعند إنقاص 25 من الحرف z، سينتج الحرف a. لتجربة ذلك، سنعلن عن متغيرين من النوع Char، ونسند لهما الحرفين، كالتالي:

وكانت نتيجة الطباعة الحرف z و الحرف a على التوالي. كما يظهر في الصورة التالية:

وضع قيمة String في حلقة for مباشرة:

يمكننا وضع قيمة السلسلة النصية مباشرة حلقة for دون استخدام المتغيرات، كالتالي:

ستكون نتيجة الطباعة للشفرة أعلاه، هي الكلمة “!Kotlin”. هذا ﻷننا داخل الحلقة، أضفنا لكل محرف من المحارف المتواجدة في السلسلة النصية، الرقم 1. فحرف J + 1 يعطينا حرف الـ K. وحرف n + 1، يعطينا حرف الـ o. وهكذا إلى آخر عنصر في السلسلة. 

نلاحظ أيضًا، أن آخر عنصر في السلسلة النصية، هو مسافة فارغة ” “. المسافة أيضًا تعتبر محرف من النوع String، طالما وضعناها بين علامتي تنصيص مزدوجة (” “). وعند إضافة الرقم 1 إليها، سينتج الرمز (!).

هذا الدرس هو جزء من سلسلة تعليم مبادئ البرمجة بلغة كوتلن. لمُتابعة الدروس منذ البداية ومُشاهدة فهرس المحتويات يمكنك الانتقال إلى الدرس الأول من هنا.

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