الحلقات (Loops) في لغة برمجة روبي

0
تعرّفنا في الدّرس السّابق على المجالات وكيفيّة استخدامها مع العبارات الشرطيّة. سنتعرّف في هذا الدّرس على أنواع الحلقات Loops المختلفة والتي يمكن استخدامها في روبي. 



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

حلقة while

أوّل نوع من الحلقات سنتعرّف عليه هو حلقة while. تقوم حلقة while بتكرار تنفيذ الشيفرة البرمجيّة الموجودة بداخلها متى صحّ شرط معيّن. لفهم الحلقة أكثر دعونا نأخذ مثالاً عليها. سنطبّق ما تعلّمناه في الدروس السّابقة ولتحسين برنامج لعبة إلقاء النّرد التي تعرّفنا عليها سابقًا. أنشئ ملفّ روبي وسمّه while.rb أو أيّ اسم تريد مع كتابة rb. في نهاية اسم الملف. واكتب الشيفرة البرمجيّة التالية به:
number = 0
attempts = 0

while number < 6
number = rand(1..6)
puts "You rolled #{number}"
attempts += 1
end

puts "It took you #{attempts} attempts to roll number 6"

بدأنا أوّلاً بتعريف متغيّر باسم number ومتغيّر باسم attempts. وأعطينا المتغيّرين قيمة مبدئيّة صفر. سنستخدم المتغيّرين لاحقًا في حلقة while لذلك فنحتاج إلى تهيئتهما أوّلاً في بداية البرنامج قبل تنفيذ حلقة while. إن لم نفعل ذلك فسوف نحصل على رسالة خطأ Error إذا تم استخدامهما قبل حصولهما على قيمة. جرّب بنفسك حذف المتغيّرين وتنفيذ البرنامج وحاول تحليل رسالة الخطأ التي ستظهر إليك. 
بعد ذلك لدينا الحلقة تبدأ باستخدام كلمة while المفتاحيّة في السّطر الرّابع وتنتهي بـ end في السّطر الثّامن (تعرّفنا على end سابقًا عندما استخدمناها مع العبارات الشرطيّة). وبين while وend لدينا الشيفرة البرمجيّة التي نريد تكرار تنفيذها متى تحقّق شرط الحلقة الموجود بعد كلمة while. لدينا الشرط في هذه الحالة هو أن تكون قيمة المتغيّر number أقلّ من 6. أعتقد أنّك قد خمّنت كل هذا بمجرّد رؤيتك للبرنامج وقبل قراءة الشرح فمن السّهولة قراءة حلقة while كما لو كانت إنجليزيّة عاديّة. ببساطة نخبر روبي أنّ تنفّذ مجموعة الشيفرة البرمجيّة متى (while) كان المتغيّر number أقلّ من 6. 
الآن وحيث أنّ قيمة المتغيّر number هي صفر في البداية فستقوم الحلقة بتنفيذ الشيفرة البرمجيّة مرّة واحدة على الأقل لأنّها ستبدأ وقيمة number أقلّ من 6.
الآن بالنّظر إلى الشيفرة البرمجيّة بداخل while والتي ستُنفّذ على الأقلّ مرّة واحدة نجد أنّها تبدأ بتعيين قيمة جديدة للمتغيّر number وهي قيمة عشوائيّة من الأرقام الصحيحة بين 1 و6. تقابل هذه الشيفرة عمليّة إلقائك للنّرد. 
بعد ذلك نستخدم أمر puts لطباعة نصّ على الشّاشة يخبر المستخدم أنّ ناتج إلقائه للنّرد هو الرقم النّاتج عن الاختيار العشوائي بين 1 و6. تقابل هذه عمليّة سقوط النّرد وظهور الرّقم النّاتج. 
في آخر سطر من هذه الحلقة سيتمّ زيادة قيمة المتغيّر attempts بواحد. تذكّر أنّ رمز =+ هذا يعني إضافة القيمة الموجودة على الجانب الأيمن إلى قيمة attempts الأصليّة وحفظ القيمة الجديدة في المتغيّر attempts. إذًا في حالتنا هذه تضيف روبي 1 إلى attempts في كلّ مرّة تُنفّذ الشيفرات. 
الآن إذا كان الرقم العشوائي النّاتج لا زال أقلّ من 6 فسيتمّ تنفيذ هذه الشيفرات الموجودة داخل حلقة while مجدّدًا، ممّا يعني تعيين رقم عشوائي جديد إلى المتغيّر number، طباعة هذه القيمة الجديدة ثمّ زيادة قيمة المتغيّر attempts بواحد. ثمّ التحقّق هل قيمة number الجديدة أقلّ من 6 وهكذا دواليك إلى أن تصبح قيمة المتغيّر number هي 6. حينها سيتمّ إيقاف الحلقة وطباعة الرسالة الموجودة في السطر 10 والتي تخبر المستخدم عدد المرّات التي ألقى فيها النّرد (والمتمثّلة في المتغيّر attempts) قبل أن يصل إلى الرّقم 6. 
جرّب تنفيذ هذا البرنامج بفتح الطرفيّة والوصول إلى المجلّد الذي يحتوي على الملفّ وكتابة أمر:
ruby [file name]

حيث [file name] هو اسم الملفّ الخاصّ بك. while.rb في حالتي. جرّب البرنامج أكثر من مرّة ولاحظ كيف تختلف المُخرجات Outputs في كلّ مرّة.

حلقة until

حلقات until تشبه إلى حدٍّ كبير حلقات while. الفرق هو أن حلقة until ستواصل التكرار إلى أن يتحقّق شرط معيّن. على عكس while التي تواصل التكرار متى كان الشرط متحقّقًا وتتوقّف عند عدم تحقّقه. 
لتوضيح الفكرة أكثر سنستعرض مثال على ذلك. أنشئ ملفّ جديد باسم until.rb أو أيّ اسم آخر واكتب به الشيفرة البرمجيّة التّالية:
puts "Hello! Type something"
input = ""

until input == "goodbye"
puts input.upcase
input = gets.chomp
end

puts "Bye! Let's play again soon"

أوّلاً بدأنا بطباعة رسالة للمستخدم ترشده إلى ما عليه فعله ومن جديد قمنا بإنشاء وتهيئة متغيّر باسم input والذي سوف يستقبل مُدخلات المستخدم. عيّننا للمتغيّر سلسلة فارغة لأن روبي ستُرجع خطأ إذا ذُكر المتغيّر للمرّة الأولى في الحلقة دون ذكر سابق له. 
الآن وبالنّظر إلى حلقة until نجد أنّ لدينا الكلمة المفتاحيّة until لبداية الحلقة وend لإنهائها. لدينا أيضًا بعد until الشرط الذي فور حدوثه ستتوقّف الحلقة عن تنفيذ الشيفرات الموجودة بداخلها. 
هذه المرّة فإنّه الشيفرة البرمجيّة الموجودة في حلقة until سيتمّ تكرار تنفيذها إلى أن (until) تكون قيمة المتغيّر input تساوي السلسلة "goodbye". 
تذكّر كيف أنّ السلسلة الافتراضيّة المعيّنة للمتغيّر input هي سلسلة فارغة، إذًا فإنّ الحلقة ستعمل على الأقلّ مرّة واحدة. 
بالنّظر إلى داخل الحلقة، نرى أنّنا نستخدم أوّلاً أمر puts لطباعة المُدخلات ونستخدم دالّة upcase لطباعة تلك المُدخلات بأحرف كبيرة Capital Letters. هل يمكن أن تخمّن ما هي القيمة الأولى التي ستُطبع عند تنفيذ البرنامج وقبل كتابتك أيّة مدخلات؟ أعتقد أنّك توقّعتها، سلسلة فارغة. 
في السطر التالي نقوم باستبدال القيمة الموجودة في المتغيّر input بالقيمة الجديدة التي سيدخلها المستخدم. تذكّر استخدمنا دالّة chomp للتخلّص من رمز Enter المضاف إلى نهاية المُدخل. 
هذه الحلقة ستُكرّر ما يُدخله المستخدم ولكن بحروف كبيرة إلى أن يدخل المستخدم goodbye. وعند حدوث ذلك فستتوقّف الحلقة عن العمل ويطبع البرنامج الرسالة الأخيرة الموجودة في السطر 9 لإخبار المستخدم بانتهاء اللّعبة. 
جرّب تنفيذ البرنامج. هل لاحظت كيف لا يطبع البرنامج كلمة goodbye بأحرف كبيرة عندما تكتبها؟ هل يمكنك جعل الحلقة تطبع goodbye بأحرف كبيرة أوّلاً ثم تتوقّف عن التنفيذ؟

المكررات

سنتعرّف الآن على بعض أنواع المكرّرات Iterators. وهي عبارة عن بعض الدوال الخاصّة بـالمصفوفات،جداول التقطيع و المجالات التي تحدّثنا عنها في الدروس السّابقة وعملها يشبه عمل الحلقات العاديّة.

دالة each

أوّل دالّة سنتعرّف عليها هي دالّة each والتي يمكنك رؤيتها في المثال التّالي:
(1..10).each do |number|
puts "#{number} squared is #{number**2}"
end

المجال الموجود هنا يمثّل الأعداد الصحيحة من 1 إلى 10 مع وجود 10 ضمنها. وبعدها استخدمنا دالّة each عن طريق كتابتها بعد المجال وبين الاثنين نقطة والتي تمثّل طريقة استدعاء الدوال على أيّ كائن في روبي. وتنتهي بالكلمة المفتاحيّة end مجدّدًا.
ما تفعله هذه الدّالّة هو تنفيذ الشيفرة البرمجيّة الموجودة بداخلها على كلّ (each) عنصر موجود في المجال. لاحظ القيمة الموجودة في السّطر الأوّل number الموجودة بين رمزي الأنبوب Pipe Symbols. تمثّل هذه القيمة ما يشبه المتغيّر المؤقّت لتخزين قيمة عنصر المجال وقت حدوث كلّ عمليّة تكرار. إذًا فستبدأ الدّالّة بتعيين قيمة للمتغيّر تساوي 1 ثمّ تُدخل تلك القيمة إلى السلسلة التابعة لأمر puts. بمجرّد تنفيذ هذا الأمر سيتمّ زيادة قيمة المتغيّر لتصبح 2 وتُنفّذ الدّالّة أمر puts من جديد. وستتابع الدّالّة فعل ذلك (أعني زيادة قيمة المتغيّر وتنفيذ أمر puts) حتّى تصل إلى أن تكون قيمة المتغيّر number هي10. إذًا فهذا المتغيّر يعتبر نوعًا ما ماسك مكان Placeholder لكلّ رقم في كلّ تكرار يحدث في الدالّة. 
الآن لنلق نظرة على الشيفرة الموجودة داخل الدّالّة. ما يفعله البرنامج هنا هو استخدام دالّة puts لطباعة النصّ الذي نريد على الشّاشة وخاصيّة الاستيفاء Interpolation لطباعة الرقم ليمكننا رؤية كل رقم في المجال بدءًا من 1 ونهايةً بـ 10 ثم يخبرنا أنّ الرقم مربّعًا يساوي قيمة الاستيفاء الثّانية {number**2}#.
تعرّفنا في درس التّعامل مع الأرقام أنّ رمز النّجمة يمثّل عمليّة الضرب. الآن أخبرك أن رمزي النّجمة معًا يمثّلان الأُسّ Power. إذًا فإنّ number**2 تعني number أسّ 2 أو قيمة number مربّعة. أو بشكل آخر number number.

دالة map

تشبه دالّة map دالّة each. ولكن بدلاً من مجرّد تنفيذ الشيفرة البرمجيّة بداخلها فإنّها تقوم أيضًا بإنشاء مصفوفة جديدة (أو جدول تقطيع) يحتوي نتائج تنفيذ الشيفرة الموجودة بداخلها على المصفوفة التي تم تنفيذها عليها. كما أنه بإمكانها باستبدال القيمة الأصليّة بداخل المصفوفة أو جدول التقطيع بالقيمة النّاتجة عن الشيفرة الموجودة داخل المكرّر أو الدّالّة map في حال ما إذا أضفنا علامة التّعجّب إلى اسم الدّالة لتصبح!map
لنستعرض المثال التّالي لتوضيح مبدأ عمل هذه الدّالّة:

fruits = ["apples", "oranges"]
fruits.map! { |fruit| "I like " + fruit }
p fruits

يمكننا أن نرى في المثال أنّ لدينا مصفوفة من سلسلتين معيّنة للمتغيّر fruits. الآن نريد أن نضيف I like قبل كلّ عنصر في هذه المصفوفة. كل ما علينا فعله هو استخدام الدّالّة map والتي سوف تكرّر تنفيذ الشيفرة البرمجيّة الموجودة بداخلها على كلّ عنصر في المصفوفة. لاحظ كيف استخدمنا الحاضنات Curly Braces هذه المرّة لوضع الشيفرة البرمجيّة بينها بدلاً من do وend. هذه الطريقة شائعة الاستخدام إذا كانت الشيفرة البرمجيّة مكوّنة من سطر واحد وذلك يوفّر عليك وضع end في سطر جديد. 
لاحظ لدينا من جديد المتغيّر المؤقّت واسمه هنا fruit والذي سيشير إلى كلّ عنصر موجود في المصفوفة. إذًا فلكلّ عنصر في المصفوفة سنقوم بإضافة السلسلة "I like" قبله. بعد ذلك استخدامنا دالّة + لإضافة السلسلة الجديدة إلى العنصر الذي تُنفّذ عليه الدّالّة. 
لن تطبع الدّالّة ذلك على الإطلاق فليس لدينا أمر puts أو أيّ شيء يؤدّي إلى تنفيذ عمليّة الطباعة. ستقوم الدّالّة فقط باستبدال العنصر الموجود في المصفوفة بالقيمة التي تُرجعها الشيفرة البرمجيّة الموجودة في الدّالّة. إذًا فنتوقّع تغيّر قيمة المصفوفة الأصليّة إلى التّالي:

["I like apples", "I like oranges"]

ولاحظ كيف استخدمنا نسخة Bang للدّالّة map. وذلك بإضافة علامة تعجّب أو ما يسمّى برمز Ban إلى اسم الدّالّة. فعل هذا يعني أن المصفوفة سوف تُستبدل بالكامل. إذا لم نضع علامة Bang فكانت الدّالّة ستقوم بتنفيذ الشيفرات على عناصر المصفوفة وإنشاء/إرجاع مصفوفة مؤقّتة ولكن لم تكن لتستبدل المصفوفة الأصليّة الموجودة في المتغيّر fruits
في حالتنا وحيث أنّنا استخدمنا Bang فسيتمّ تغيير المصفوفة الأصليّة وحفظها في fruits
السّطر الأخير من الشيفرات يستخدم أمر p والذي يستخدم هنا لإظهار كيف أصبح المصفوفة بعد تغييرها. 
يستخدم أمر p غالبًا عند تنقيح الشيفرة البرمجيّة. حيث أنّه سيُظهر الكائن بدلاً من تحويله إلى سلسلة. هو مشابه جدًا لأمر puts. ولكن في حين أنّ puts يطبع دائمًا سلسلة على الشّاشة حيث أنّه اختصار لـ put string، فإنّ أمر يُظهر الكائن تمامًا كما هو. إذًا فإنّه في المثال أعلاه بإمكاننا باستخدام p لرؤية المصفوفةfruits كمصفوفة. 
جرّب تنفيذ البرنامج وألق نظرة على المصفوفة الجديدة بعد التّعديل.

خاتمة

تعرّفنا في هذا الدّرس على مفهوم الحلقات وأنواعها ويمكنك التعرّف عليها أكثر عن طريق قراءة توثيق روبي عنها. تعرّفنا أيضًا على المكرّرات المستخدمة مع المصفوفات، جداول التقطيع والمجالات. أقترح عليك تحسين أحد البرامج المستخدمة في هذا الدّرس، مثلاً جرّب تعديل المثال المستخدم في حلقة until بحيث يستقبل كلمة goodbye على أيّ هيئة ويتحقّق الشرط وتتوقف الحلقة عن التنفيذ (أعني عند كتابة المستخدم Goodbye ،GOODBYE أو GoodBye أو غير ذلك من صور الكلمة يعتبر البرنامج أنّ الشرط قد تحقّق ويوقف اللّعبة). إذا كان لديك تساؤل أو تريد مشاركتنا ما طبّقت لا تتردّد في استخدام قسم التعليقات أدناه.