المصفوفات (Arrays) في روبي

0
بعض أنواع البيانات التي تحدّثنا عنها في الدروس السّابقة هي السلاسل و الأرقام. تتيح لنا السلاسل إمكانيّة التعامل مع النصوص الخاصّة ببرنامجنا والأرقام تمكننا من التعامل مع البيانات الرقميّة.


لنفترض مثلاً أنّنا نريد إنشاء قائمة مشتريات، بداخل تلك القائمة لدينا أسماء المشتريات. يمكننا إنشاء تلك القائمة على شكل سلسلة String مفصول بين كل عنصر فيها بفاصلة. أو يمكننا استخدام ثلاثة سلاسل محفوظة بمتغيّرات مسمّاة item1, item2 وهكذا. ولكن لحسن الحظّ فإنّ روبي تُوفّر لنا نوعًا يمكنه التعامل مع هذا النّوع من البيانات فعلاً يسمّى بالمصفوفة Array. المصفوفة هي عبارة عن حاوية للبيانات. تُستخدم المصفوفات لتخزين أنواع مختلفة من البيانات مثل السلاسل، الأرقام وأيّ نوع آخر من كائنات روبي. سنتعرّف في هذا الدّرس على كيفيّة إنشاء المصفوفات والتّعامل معها في روبي.

إنشاء مصفوفة

سنستخدم سطر أوامر  روبي التفاعليّ الآن للتعرّف على المصفوفات. ابدأ جلسة روبي في الطرفيّة عن طريق كتابة irb والضّغط على Enter. 
يمكن إنشاء مصفوفة حرفيّة Array Literal عن طريق وضع الكائنات داخل أقواس مربّعة Square Brackets مفصول بينها بفاصلة. كالمثال أدناه، أنشأنا قائمة من الأعداد الأوّليّة:
[2, 3, 5, 7]
لا تشترط المصفوفات بأن تقوم بتخزين أرقام فقط بها. يمكنك أيضًا إنشاء مصفوفة مكوّنة من سلاسل هكذا:
["apples", "oranges"]
هناك أيضًا طريقة أقصر لإنشاء مصفوفة من السلاسل. نبدأ ذلك بكتابة علامة النّسبة المئويّة متبوعة بحرف w ثمّ بعد ذلك يمكنك الاختيار إذا كنت تريد استخدام الأقواس Parentheses أو الحاضنات Curly Brackets لبدء المصفوفة، الأمر الإيجابي حول هذه الطريقة هو أنّه باستخدامها لن تصبح في حاجة إلى وضع كل سلسلة بين علامات اقتباس وأيضًا لن تحتاج إلى الفصل بين عناصر المصفوفة باستخدام الفاصلة، استخدام المسافات يفي بالغرض.
%w(apples, oranges)
%w{apples oranges}
عند كتابة المصفوفة بأحد الطريقتين أعلاه والضّغط على Enter ستلاحظ أنّ روبي ستضعها في الصّورة الافتراضيّة للمصفوفة، أقواس مربّعة ومفصول بين السلاسل بفاصلة. 
ليس مفروضًا عليك استخدام كائنات من نوع بيانات واحد في المصفوفة فيمكنك إنشاء مصفوفة تحتوي على أكثر من نوع من العناصر، كما تلاحظ في المثال أدناه فقد أنشأنا مصفوفة تحتوي على عدد صحيح Integer، سلسلة وعدد عشري Float:
[1, "two", 3.0]
هناك طريقة أخرى لإنشاء مصفوفة وذلك باستخدام دالّة new كالتالي:
fruits = Array.new
ما فعلناه هنا هو إنشاء متغيّر باسم fruits وتعيين مصفوفة جديدة فارغة إلى ذلك المتغيّر باستخدام دالّة new
قد تتساءل، إذا كان بالإمكان تعيين مصفوفة إلى متغيّر فهل يمكن استخدام متغيّر كعنصر في مصفوفة؟ نعم يمكن ذلك ويكون نوع العنصر هو نوع البيانات الموجودة في المتغيّر، جرّب إنشاء متغيّر ثم ضعه في المصفوفة كعنصر كما فعلت سابقًا مع السلاسل، تسمّى هذه العمليّة بالاستيفاء Code Interpolation.
item = "apples"
fruits = %W(#{item} oranges)
لاحظ أنّنا استخدمنا حرف W كبير بدلاً من الصّغير الذي استخدمناه سابقًا.

المصفوفات متعددة الأبعاد

يمكنك إنشاء مصفوفة تحتوي على مصفوفات أخرى بداخلها، تسمّى بالمصفوفة متعدّدة الأبعاد Multidimensional Array. يعد هذا النوع من المصفوفات مفيدًا لإنشاء مستوى إحداثيّات. يمكن إنشاء مصفوفة متعدّدة الأبعاد هكذا:
[[1, 3], [5, 7]]

الوصول إلى عنصر أو عدة عناصر بالمصفوفة

تعدّ المصفوفات أحد أدوات روبي المهمّة. وتمتلك المصفوفات بعض الدوال المفيدة للوصول إلى عنصر معيّن بها. للتعرف على بعض تلك الدوال سنقوم بإنشاء مصفوفة بقائمة المشتريات تحتوي على 5 عناصر. لنقم بإنشاء متغيّر باسم list وتعيين المصفوفة التي نريد إلى ذلك المتغيّر.
list = %w(apples oranges milk bread sugar)
للوصول إلى قيمة معيّنة في المصفوفة نكتب مكان وجود هذه القيمة في أقواس مربّعة، مكان تواجد القيمة يسمّى دليل index. إذًا إذا أردت أن أجد العنصر الموجود في المكان الأوّل من المصفوفة نكتب اسم المصفوفة، الأقواس المربّعة ورقم الدليل بداخلها هكذا:
list[1]
ولكن ماذا أعادت لنا روبي عند تنفيذ هذا؟ أعرف أنّك لم تتوقّع أن تعيد orangesبدلاً من apple. لماذا حدث ذلك لأنّ الترقيم في المصفوفات يبدأ من 0 وليس 1 فإذا أردنا الوصول إلى أوّل عنصر على الإطلاق في المصفوفة نكتب ذلك كالتالي:
list[0]

القيم السالبة

يستخدم نظام الترقيم الخاص بالمصفوفات قيمًا سالبة أيضًا وذلك إذا أردنا بدء العدّ من آخر المصفوفة. إذًا إذا أردت الوصول إلى آخر عنصر في المصفوفة يمكنك استخدام -1.
list[-1]
هناك أيضًا دوال مدمجة جاهزة للوصول إلى أوّل وآخر عنصر في المصفوفة.
list.first
list.last

دالة fetch

يمكن استخدام دالّة fetch مع الدليل لإرجاع قيمة العنصر الموجود بهذا الدليل.
list.fetch(2)   # "milk"
إذا تم كتابة معطى ثاني في الدّالّة ولا يوجد هناك العنصر الذي يشير إليه الدليل المحدّد فسيتمّ إرجاع القيمة الموجودة في المعطى الثّاني كقيمة افتراضيّة. مثال:
list.fetch(20, "Not found") # "Not found"

المصفوفة الجزئية

يمكننا أيضًا إرجاع مصفوفة جزئيّة Subset Array من مصفوفة عن طريق كتابة معطى Parameter إضافي مع الدليل. يشير هذا المعطى إلى طول المصفوفة الجزئيّة التي نريد إرجاعها. إذًا إذا أردنا إرجاع أوّل ثلاثة عناصر في المصفوفة نكتب ذلك كالتالي:
list[0, 3]  # ["apples", "oranges", "milk"]
طريقة أخرى لإرجاع مصفوفة جزئيّة هو استخدام مجال معيّن range.
list[0..2]  # ["apples", "oranges", "milk"]

دالة include

لمعرفة إذا كانت مصفوفة تحتوي على كائن معيّن نستخدم دالّة include والتي تستقبل معطى باسم الكائن الذي نريد التحقّق من وجوده. وحيث أنّ الدّالّة منطقية (بمعنى أنّها ترجع true أو false فقط) فإنّنا نكتب في نهايتها علامة استفهام، ?include. إذًا لو أردنا معرفة إذا كانت تحتوي مصفوفة list على سلسلة apples نكتب ذلك كالتالي:
list.include?("apples")

معرفة عدد عناصر المصفوفة

إذا أردت معرفة كم عدد العناصر بالمصفوفة فيمكن استخدام دالّة size والتي تقوم بإرجاع عددًا صحيحًا يمثّل عدد العناصر الموجودة بالمصفوفة.
list.size
اسم آخر لدالّة size هو length. جرّب كتابة الأمر التالي ولاحظ كيف تمّ إرجاع نفس النتائج.
list.length

إضافة عناصر إلى المصفوفة

دالة push

ماذا لو أردنا إضافة شيء إلى المصفوفة؟ يمكننا إضافة سلسلة تحتوي على "cheese" إلى نهاية المصفوفة باستخدام دالّة push. تقوم الدّالّة بإلحاق السلسلة التي نريد إضافتها بنهاية المصفوفة.
list.push("cheese")
هناك عامل اختصار Shorthand Operator لفعل نفس الوظيفة وهي أقواس الزاوية المزدوجة Double Angle Brackets (<<). فمثلاً إذا أردنا إضافة سلسلة "juice" إلى مصفوفة list بطريقة أسرع نكتب ذلك هكذا:
list << "juice"
أسهل وأسرع بكثير من دالّة push أليس كذلك؟ والأمر اللّطيف أيضًا حول ذلك أن هذه الأسهم تشير إلى مصفوفة list كما لو كانت تخبر روبي بإضافة السلسلة إلى تلك المصفوفة. 
طريقة أخرى لإضافة عناصر إلى آخر المصفوفة هو استخدام معامل =+ والذي يعني أن تجعل المصفوفة تساوي نفسها (=) بالإضافة إلى (+) القيمة الموجودة في الطرف الأيمن.
list += ["bananas", "cereals"]
الشيفرة البرمجيّة أعلاه تطلب من روبي بأن تضيف السلسلتين "bananas" و"cereals" إلى نهاية المصفوفة list.

دالة unshift

إذا أردنا إضافة عنصر إلى بداية المصفوفة وليس نهايتها فيمكننا استخدام دالّة unshift والتي تعمل عمل push مع فرق أنّها تضيف العنصر إلى بداية المصفوفة.
list.unshift("carrots")

حذف عناصر من المصفوفة

دالة pop

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

دالة shift

دالّة shift مشابهة لدالّة unshift والتي تضيف عنصر إلى بداية الدّالّة، الفرق الوحيد هو أنّ دالّة shift تقوم بإرجاع وحذف العنصر الأوّل من المصفوفة. أو بعبارة أخرى، هي تقوم بنفس عمل دالّة pop لكن على بداية المصفوفة.
list.shift

دالة drop

يمكننا استخدام دالّة drop لحذف عدد من العناصر من مصفوفة معًا. المعطى الخاصّ بالدّالّة هو عدد العناصر المطلوب حذفها من بداية الدّالّة.
list.drop(2)    # تقوم بحذف عنصرين من بداية الدّالّة

دالة !slice

قد تتساءل ماذا لو أردت حذف عناصر ليست في بداية أو نهاية المصفوفة. يمكننا في هذه الحالة استخدام دالّة !slice. تأخذ دالّة !slice المعطى الأول هو رقم الدليل المطلوب البدء منها والمعطى الثّاني هو عدد العناصر المطلوب حذفها.
list.slice!(0, 3)
لاحظ أنّه في حال ما إذا لم نضف علامة التّعجّب في نهاية اسم الدّالة فإننا سنحصل على جزء من المصفوفة مثلما هو مُتوقّع، لكنّه لن يتم حذف العناصر من المصفوفة الأصلية

دالة sort

يمكننا ترتيب مصفوفة باستخدام دالّة sort. الأمر التالي سيؤدّي إلى ترتيب المصفوفة استنادًا إلى الحروف الأبجديّة:
list.sort
ستلاحظ أنّه تمّ إرجاع المصفوفة وعناصرها مرتّبة أبجديًّا. ولكن لم يغيّر ذلك شيئًا في ترتيب المصفوفة الأصلي، يمكنك التحقّق من ذلك بنفسك. فقط اكتب اسم المصفوفة واضغط Enter. ستجد أنّ المصفوفة الأصليّة لم تتغيّر. 
ولكن إذا أردت ترتيب المصفوفة وتغيير المصفوفة الأصليّة ماذا نفعل؟ يمكننا الآن الرجوع إلى دوال Bang التي تحدّثنا عنها في درس السلاسل والتي تقوم بتعديل القيمة الأصليّة للكائن ونستدعيها عن طريقة كتابة اسم الدالّة الأصليّة متبوعة بعلامة تعجّب.
list.sort!

دالة reverse

يمكننا استخدام دالّة reverse من أجل طباعة المصفوفة معكوسة. هذه المصفوفة أيضًا لا تغيّر من قيمة المصفوفة الأصليّة للأبد، ولكن إن أردنا تغيير المصفوفة الأصليّة فلدينا دالّة reverse!.
list.reverse

دالة join

دالّة أخيرة سنتعرّف عليها هي دالّة join. هذه الدّالّة مفيدة جدًّا لأنّه يمكن استخدامها لإنشاء سلسلة تقوم بدمج جميع عناصر مصفوفة معًا. تستقبل الدّالّة معطى يحتوي على الرّمز الذي تريد استخدامه كفاصل. مثلاً الأمر التالي:
list.join(",")
ماذا فعلنا هنا؟ لقد استدعينا دالّة join على مصفوفة list ومعطى الدّالّة هو سلسلة تحتوي على فاصلة والتي ستستخدم كفاصل بين عناصر المصفوفة. عند تنفيذ هذا الأمر ستطبع لك روبي على الشّاشة سلسلة تحتوي على جميع العناصر وبين كل عنصر والآخر الفاصلة التي أردنا استخدامها.

خاتمة

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