أستمع الى المقال

شرحنا في الدرس السابق، التجميعات Collections وكيفية استخدامها في تجميع عدد من الكائنات من نفس النوع معا، عبر استخدام إحدى الأنواع الثلاث للتجميعات في كوتلن. في هذا الدرس، سنستعرض أول هذه الأنواع، ألا وهي القائمة List.

القوائم Lists:

هي عبارة عن تجميعة Collection مرتبة من العناصر من نفس نوع البيانات عادةً. مثل أن ننشئ قائمة تحوي نوع من أنواع البيانات الأساسية، مثل:

Int, Long, Double, Float, Char, String, Byte, or Boolean.

أو حتى يمكننا إنشاء قائمة تحوي نوع بيانات (صنف) خاص بنا، كما سنرى لاحقاً في هذا الدرس.

وتوفر القائمة List إمكانية وصول إلى عناصرها عن طريق الفهارس Indices – وهي أعداد صحيحة موجبة تبدأ من الصفر، تؤشر على موضع العنصر في القائمة. ويمكن أن تتكرر العناصر في قائمة List، أكثر من مرة.

إنشاء قائمة List في كوتلن:

يمكننا إنشاء قائمة List، عبر:

استخدام الدالة List:

val list = List(size) { elements }

سنضع عدد العناصر في مكان كلمة size بين قوسي دالة List. أما بين الأقواس المعقوفة { }، نضع العناصر نفسها، ونفصل بينها ب ( , ).

استخدام دالة ()listOf:

val list = listOf<DataType> ( elements )

عند استخدام دالة ()listOf، يمكننا وضع نوع بيانات عناصر القائمة بين قوسي زاوية < >. ثم بين أقواس الدالة، نضع العناصر ونفصل بينها ب ( , ).

إذا أردنا إنشاء قائمة تحوي أسماء أنواع متعددة من الفواكه، يمكننا إنشاؤها في كوتلن، كالتالي:

val fruits = listOf<String>(“Orange”, “Apple”, “Banana”, “Mango”, “Avocado”)

أعلنا عن متغير عادي باستخدام val أسميناه fruits. ثم لنتمكن من إسناد مجموعة من كائنات الـ String التي تُمثل أسماء مختلفة للفواكه إلى المتغير fruits، استخدمنا دالة ()listOf التي تأتي مجهزّة مع حِزمة كوتلن، والتي تُعيد كائن من النوع قائمة List، يحتوي على الكائنات التي يتم إرسالها إليها. وحددنا نوع بيانات عناصر (كائنات) القائمة بين قوسي الزاوية، بالنوع String.

بعد التصريح بنوع البيانات بين قوسي الزواية، نكون قد حددنا بالضبط نوع العناصر التي ستحتويها القائمة. لذلك لن تقبل قائمة fruits في هذه الحالة، إلا كائنات من نوع البيانات String. سنتعلم المزيد عن قوسي الزاوية لاحقاً في هذه الدورة.

هكذا نكون قد حصلنا على كائن واحد اسمه fruits، يُمثل قائمة تحوي أسماء 5 أنواع من الفواكه. 

الاستدلال على نوع بيانات القائمة:

في معظم الحالات، تستنتج كوتلن نوع بيانات القائمة تلقائيا، من خلال نوع بيانات العناصر التي نضعها داخلها. ففي المثال أعلاه، ستتعرف كوتلن على نوع بيانات القائمة fruits، على أنها قائمة تحتوي عناصر من نوع البيانات String. بالتالي، سيكون نوع بيانات المتغير fruits هو <List<String.

لذلك يمكننا التخلي عن ذكر نوع بيانات القائمة صراحة، كالتالي:

val fruits = listOf(“Orange”, “Apple”, “Banana”, “Mango”, “Avocado”)

بعد إنشاء القائمة، نحتاج إلى طريقة للوصول إلى هذه العناصر والتعامل معها. ونستطيع الوصول إلى هذه العناصر، باستخدام المتغير fruits، والفهرس Index.

الفهرس Index:

لدى كل قائمة List حين يتم إنشاؤها، فهرس يبدأ دائماً بالرقم 0. كالتالي:

كما يظهر في الصورة أعلاه، يكون فهرس العنصر الأول في القائمة 0، والعنصر الثاني 1، وهكذا إلى آخر عنصر في القائمة. 

ولأن الفهرس يبدأ بالرقم 0، نجد أن العنصر الأخير في قائمة fruits، والتي بها 5 عناصر، يكون فهرسه الرقم 4. بالتالي، يكون فهرس العنصر الأخير يساوي حجم size (أو عدد) العناصر في القائمة ناقص 1. أي 5 – 1 في قائمة fruits.

الوصول إلى عناصر القائمة:

للحصول على العنصر الثالث مثلاً في قائمة fruits، يمكننا استخدام دالة ()get:

fruits.get(2)

نكتب اسم القائمة ثم نقطة ثم دالة ()get ونضع بين قوسيها رقم الفهرس للعنصر الثالث. وﻷن الفهرس في القائمة List يبدأ من 0، سيكون فهرس العنصر الثالث هو الرقم 2. يمكننا وضع السطر أعلاه في دالة الطباعة ﻷنه عبارة عن تعبير ويُعيد قيمة:

println(fruits.get(2))

عند تنفيذ الشفرة، سيتم طباعة القيمة Banana، وهي قيمة العنصر الثالث في قائمة fruits.

وبدلًا عن استخدام دالة ()get، يمكننا أيضًا استخدام أقواس الفهرس المربعة – [ ] – ووضع رقم فهرس العنصر داخلها:

println(fruits[2])

أي الطريقتين استخدمنا، سيتم إعادة قيمة العنصر الذي يُمثله الفهرس. مع العلم أن الطريقة الأخيرة، هي الأكثر استخدامًا، لذلك هي ما سنستخدمها في هذه الدورة.

التهيئة:

تعتبر القائمة List نوع بيانات عام. أي يمكنها قبول أي نوع بيانات آخر، سواء كان من الأنواع الأساسية في كوتلن، أو صنف خاص بنا. فكما رأينا في المثال السابق، يمكننا تهيئتها بمساعدة الدالة <listOf<E، حيث E هو نوع بيانات العناصر التي ستحتويها القائمة، أيا كان هذا النوع.

إنشاء قائمة List من أنواع البيانات الأساسية:

بالتالي، لإنشاء قائمة باستخدام أنواع البيانات الأساسية في كوتلن، نفعل كما يظهر في الصورة التالية:

نلاحظ أنه في قائمة bool والتي تحتوي على عناصر من النوع Boolean، يمكننا وضع true أو false مباشرةً، أو يمكننا وضع بعض التعابير التي ستعيد true أو false. 

فمثلًا، 1==1 ستكون النتيجة true. وكذلك الحال مع التعبير 0 == 5/5. ثم استخدمنا دالة ()contains على قائمة numbers، ﻷن لو العنصر 29 موجود بالقائمة، ستعيد الدالة النتيجة true. أما إذا لم يكن متوفرًا، ستعيد النتيجة false.

إنشاء قائمة List من النوع (الصنف) الخاص بنا:

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

أنشأنا صنف class أسميناه FruitsClass وبه معامل باني واحد والذي يكون خاصية في نفس الوقت. (شرحنا هذا الأمر في درس الباني Constructor). ثم في دالة ()main، اسندنا كائنات لعدد من أنواع الفواكه إلى متغيرات.

وعند إنشاء القائمة، أخبرنا دالة ()listOf صراحةً بأن القائمة ستحتوي على كائنات من النوع FruitsClass بوضع اسم النوع بين قوسي زاوية. يمكننا إرسال المتغيرات التي تُمثل كائنات FruitsClass، أو وضع الكائن نفسه داخل قوسي الدالة. في كلا الحالتين ستُعيد الدالة، قائمة من الكائنات FruitsClass.

نلاحظ أيضًا، أننا كررنا الكائن Apple والذي يمثله المتغير fruit2 داخل القائمة. ﻷنه يمكننا فعل ذلك مع النوع List من التجميعات، خلاف النوع Set، كما أوضحنا في درس التجميعات.

وفي نهاية الشفرة، استدعينا ثلاث دوال طباعة، لطباعة أسماء العناصر: الأول والثالث والأخير في قائمة fruits. والتي تمكنا من الوصول إليها، عبر استخدام الخاصية name. ﻷن كل كائن ننشئه من الصنف FruitsClass، ستتوفر له هذه الخاصية. كما يتضح في الصورة التالية:

الخصائص والدوال:

توفر كوتلن مجموعة من الخصائص والدوال للتعامل مع القائمة List. منها الدالة ()get والتي يمكن استبدالها بأقواس الفهرس [ ]، والدالة ()contains.

ثم هناك خاصية size، والتي تعيد عدد العناصر في القائمة. وأيضًا دالة ()isEmpty لمعرفة ما إذا كانت القائمة فارغة أم لا. ويمكننا أيضًا استخدام دالة ()isNotEmpty لنفس الغرض. إذا طبقنا كل هؤلاء على قائمة fruits، تكون النتيجة:

استخدمنا دالة ()isNotEmpty على قائمة fruits، ووضعناها كشرط لتنفيذ الشفرة داخل كتلة if. وﻷن القائمة بها عناصر وليست فارغة، ستعيد الدالة النتيجة true، لذا سيتم تنفيذ شفرة if.

داخل if، أعلنا عن متغير fruitsSize وأسندنا إليه قيمة عدد عناصر القائمة. ثم متغير bestFruit وأسندنا إليه قيمة أول عنصر. وفي المتغير  sportFruit اسندنا قيمة آخر عنصر في القائمة والذي وصلنا إليه باستخدام عدد عناصر القائمة ناقص واحد.

وفي دالة الطباعة، استفدنا من طريقة القوالب النصية في كوتلن، باستخدام علامة التنصيص الثلاثية، لطباعة رسالة بطريقة قابلة للقراءة ومرتبة.

ولأن علامة التنصيص الثلاثية ستطبع كل ما بداخلها بما فيها الفراغات التي في بداية كل سطر، استخدمنا دالة ()trimIndent لحذف هذه الفراغات. 

وعند تنفيذ الشفرة ستكون النتيجة:

قوائم قابلة للتغيير Mutable Lists:

عند إنشاء قائمة باستخدام دالة ()listOf، نحصل تلقائيًا على نوع قائمة ثابت غير قابل للتغيير Immutable List. سيكون هذا النوع من القوائم مفيدًا، إذا كنا نعرف عدد العناصر بالضبط، ولن نحذف أو نضيف عناصر للقائمة لاحقًا.

أما إذا أردنا قائمة مرنة توفر كل ذلك، سنقوم بإنشائها عبر استخدام دالة ()mutableListOf:

val fruits = mutableListOf(“Orange”)

اكتفينا بوضع عنصر واحد فقط في القائمة، ﻷنه يمكننا إضافة عناصر لها لاحقًا.

دوال إضافية للقوائم القابلة للتغيير:

كل الدوال السابقة التي استخدمناها مع نوع القوائم الثابت، يمكننا استخدامها مع هذا النوع، بالإضافة إلى دوال لإجراء عمليات الإضافة والحذف وغيرها. مثل: دالة ()add لإضافة عنصر جديد للقائمة، ودالة ()remove لحذف عنصر، ودالة ()removeAt والتي نرسل لها رقم الفهرس للعنصر لحذفه:

بدأنا القائمة بعنصر واحد وهو “Orange”. وعند طباعة القائمة، ستكون النتيجة:

[Orange]

بعد ذلك، أضفنا إليها ثلاث عناصر. وعند طباعة القائمة مرة أخرى، ستكون النتيجة:

[Orange, Apple, Banana, Mango]

تم إضافة العناصر الثلاث للقائمة.

ثم عدنا وحذفنا العنصر “Orange” باستخدام قيمته. وعند الطباعة:

[Apple, Banana, Mango]

اختفي العنصر من القائمة. وأصبح العنصر “Apple”، هو صاحب الفهرس 0، ﻷنه أصبح العنصر الأول في القائمة.

وأخيرًا، حذفنا العنصر الأول من القائمة، باستخدام فهرسه وهو الرقم 0:

[Banana, Mango]

وهو ما يظهر عند تنفيذ الشفرة أعلاه، في تبويب Run في برنامج IntelliJ:

ولحذف كل عناصر القائمة، يمكننا استخدام دالة ()clear:

fruits.clear()

يمكن إيجاد كل الخصائص والدوال التي يمكننا استخدامها مع Map، في هذا الرابط من موقع كوتلن الرسمي.

قراءة قائمة من الإدخال القياسي:

بدلاً من وضع عناصر القائمة بأنفسنا كما فعلنا في قائمة fruits، يمكننا تمكين المستخدم من إدخال قائمة من الفواكه التي يحبها، عبر استخدام الإدخال القياسي Standard Input. ثم قراءة هذه العناصر عبر دالة ()readln في كوتلن. 

ولكن قبل ذلك، يجب علينا أن نحدد عدد العناصر التي ستحتويها القائمة، كالتالي:

val fruits = MutableList(5) { readln() }

لإنشاء قائمة محددة العناصر مسبقًا، نستخدم دالة ()MutableList ثم نضع عدد عناصر القائمة بين قوسيها. ثم نفتح قوسين معقوفين لنضع بهما قيم عناصر القائمة. وسيكون عمل دالة ()readln هو قراءة عدد 5 أسطر من مدخلات المستخدم، حيث يُمثل كل سطر عنصر في القائمة، كما يظهر في الصورة التالية:

يجب إدخال عنصر واحد ثم الضغط على Enter من لوحة المفاتيح ثم عنصر آخر ثم Enter… وهكذا حتى العنصر الخامس، عندها سينتهي عمل الدالة ()readln ويتم تعبئة القائمة بالعناصر الجديدة. 

ولكن ماذا لو أردنا إدخال كل العناصر في سطر واحد؟ حسنًا في هذه الحالة، يجب أن نفصل split العناصر بفاصل معين ونخبر الدالة ()readln به، عبر استخدام دالة ()split. ثم بعدها نحوِّل المدخلات إلى قائمة عبر دالة ()toMutableList. فمثلًا، يمكننا استخدام مسافة فارغة كفاصل:

الدوران على عناصر القائمة:

لإجراء عمليات على كل عنصر في قائمة List أو MutableList على حدى، يمكننا الدوران عليها باستخدام حلقة التكرار for:

وستكون نتيجة تنفيذ الشفرة:

برنامج قائمة فواكه الأسبوع:

لفهم كل السابق، وكخلاصة لهذا الدرس وبعض المفاهيم التي وردت في دروس سابقة في هذه الدورة، فلنقم بكتابة برنامج يستقبل مدخلات من المستخدم ثم يدمجها في قائمة، ويطبع النتيجة.

البرنامج سيطلب من المستخدم، إدخال قائمة الفواكه لمدة أسبوع، بواقع نوع واحد من الفواكه لكل يوم. ثم سيحتفظ بالمدخلات في قائمة MutableList، وأخيرًا يطبع محتوى القائمة في رسالة مفهومة للمستخدم:

لفهم شفرة البرنامج، فلنلقي نظرة على الصورة من برنامج IntelliJ أدناه:

  1. بداية في أعلى الدالة الرئيسية ()main، قمنا بالإعلان عن قائمة MutableList وحددنا لها النوع WeekFruits وهو الصنف الذي كتبناه تحت الدالة.
  2. استخدمنا حلقة التكرار while، ﻷننا نريد من المستخدم أن يدخل عدد معروف من العناصر محدد بـ 7 عناصر. وسنضمن دوران الحلقة لـ 7 مرات فقط، عبر مراقبة قيمة المتغير x. ففي كل دورة ستزيد قيمة المتغير x واحد، حتى تصل قيمته إلى 8، وهنا ينتفي الشرط (x < 8) لذا سينتهي عمل while.

أما المتغير day، فسيبدأ بالرقم 1 وهو الرقم الذي يُمثل الكلمة “Monday” في دالة ()getDay التي كتبناها خصيصًا لهذا الغرض. ثم مع كل دورة لحلقة while، ستزيد قيمته واحد حتى الرقم 7. 

وداخل الحلقة while ايضًا، نطبع رسالة للمستخدم. ثم نقرأ اسم الفاكهة عبر دالة ()readln، وننشئ كائن فاكهة عبر ارسال الاسم مرفقًا مع رقم اليوم الموجود في متغير day إلى الصنف WeekFruits. ثم عبر باستخدام دالة ()add، نضيف الكائن إلى قائمة fruits. وفي كل دورة جديدة للحلقة، سيتم إضافة كائن جديد للقائمة.

  1. مهمة دالة ()getDay هي إرجاع قيمة نصية بإسم اليوم حسب الرقم الذي يتم إرساله إليها.
  2. صنف WeekFruits لحفظ بيانات كائن فاكهة واحد. الاسم name ورقم اليوم day.
  3. ثم طباعة عناصر القائمة في رسالة مفهومة للمستخدم عبر استخدام طريقة القوالب النصية في كوتلن. دالة ()uppercase من مكتبة كوتلن القياسية، تحول النص إلى حروف إنجليزية كبيرة.
  4. مدخلات المستخدم. تم التكرار لـ 7 مرات عبر شفرة while التي شرحناها في الخطوة رقم 2.
  5. نتيجة الطباعة من الخطوة رقم 5.

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

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