تعلّم البرمجة بلغة كوتلن (47): العمليات على التجميعات Collections

استمع إلى المقال
|
شرحنا في الدرس السابق، بعض الفروقات بين نمطي البرمجة الأمرية والوظيفية. ودرسنا أيضًا بعض تطبيقات البرمجة الوظيفية في كوتلن، مثل كيفية إنشاء تعابير ودوال اللامبدا. وكيفية استخدامها بإسنادها لمتغير، أو إرسالها كقيمة Argument، إلى دالة أخرى. هذه الدوال الأخرى التي نُرسل إليها تعابير اللامبدا، يجب أن يكون لديها معامل Parameter من نوع بيانات الدوال.
ولأننا لم ندرس بعد كيفية إنشاء دوال خاصة بنا يكون لديها معامل من نوع بيانات الدوال، استخدمنا حتى الآن، دوال مهمة ومستخدمة بكثرة من مكتبة كوتلن القياسية. مثل: ()filter، و ()partition، و ()map. (لاحقًا في درس قادم في هذا القسم، سنشرح تفصيليًا كيفية إنشاء هذه الدوال والتعامل معها).
لدى كل دالة من هذه الدوال الثلاث مهمة معينة تقوم بها عند استدعائها مع القوائم Lists، كما رأينا عمليًا في درس دوال اللامبدا. في هذا الدرس، سنواصل استعراض المزيد من هذه الدوال، التي لديها معامل من نوع بيانات الدالة، وتقبل تعابير اللامبدا، وكيفية استخدامها مع القوائم Lists وغيرها من التجميعات، وما الناتج الذي نتوقعه منها.
يتمثل أحد الجوانب الأساسية المهمة للغات البرمجة التي تستخدم نمط البرمجة الوظيفية، هي القدرة على إجراء عمليات مجمعة بسهولة على تجميعة من الكائنات. إذ توفر معظم هذه اللغات الوظيفية، دعمًا قويًا للعمل مع التجميعات، ولغة كوتلن ليست استثناءً. من هذه العمليات في كوتلن، يمكننا الإعلان عن أو تهيئة التجميعات، عبر استخدام اللامبدا.
رأينا سابقًا في درس القوائم، كيف يمكننا الإعلان عن قائمة List عبر استخدام دالتي: ()listOf للقوائم غير القابلة للتغيير و ()mutableListOf للقوائم القابلة للتغيير. أمّا الإعلان عن قائمة باستخدام تعبير اللامبدا، فسيكون كالتالي:
val listName = List(size, { elements } )
لدى دالة ()List (خلافاً للدوال العادية، تبدأ بحرف كبير) معاملين، الأول يستقبل قيمة حجم size (عدد عناصر) القائمة، والمعامل الثاني هو تعبير اللامبدا الذي نضع به قيمة العناصر. ولأن معامل اللامبدا هو المعامل الأخير في معاملات دالة ()List، يمكننا كتابته خارج أقواس الدالة، كما وضّحنا في الدرس السابق:
val listName = List(size) { elements }
تعبير اللامبدا في دالة ()List لديه معامل واحد من النوع Int وهو يُمثِّل الفهرس في القائمة المُراد إنشاؤها. لذا يمكننا استخدامه هكذا:
السطر أعلاه سينتج قائمة تكون عناصرها هي قيمة الفهرس index. ولأن الفهرس يبدأ من الصفر، كذلك العناصر ستبدأ من الصفر وتستمر إلى آخر عنصر حسب الـ size المُعطى للدالة.
أو يمكننا استبدال المعامل index بالكلمة it:
val listName = List(size) { it }
سنفهم هذا الأمر عمليًا في الفقرتين التاليتين.
لفهم طريقة عمل دالة ()List، فلنلقي نظرة على المثال التالي:
في قائمة list1، أنشأنا قائمة بالحجم 10. أمّا العناصر، فستكون قيمتها هي قيمة فهرس القائمة، والذي تُمثله الكلمة it في تعبير اللامبدا.
وفي قائمة list2، أنشأنا قائمة بعدد 10 عناصر قيمة كل عنصر منها هي العدد صفر.
وفي list3، استخدمنا اللامبدا لإيجاد قيمة العناصر الخمسة. فكما نعرف، ان كل حرف يُمثَّل برقم في الحاسب، استفدنا من ذلك في إنشاء عناصر القائمة، بجمع قيمة الحرف الرقمية و قيمة رقم الفهرس في القائمة الجديدة.
وعند تنفيذ الشفرة، ستكون النتيجة:
بنفس الطريقة، يمكننا إنشاء القوائم القابلة للتغيير MutableList، باستخدام دالة ()MutableList:
القائمة التي سيتم إسنادها إلى المتغير decades، ستتكون عناصرها من نتيجة العملية الحسابية داخل أقواس اللامبدا المعقوفة:
{ 10 * (it + 1) }
أي الرقم 10 مضروب في رقم فهرس القائمة. ولأن فهرس القائمة يبدأ من صفر، لذا أضفنا له 1 حتى تكون بداية عملية الضرب من العدد 1.
أمّا القائمة التي سيتم إسنادها للمتغير positiveToNegative، استخدمنا عناصر القائمة decades لإنشاء عناصرها. التعبير التالي، سينتج لنا نفس عناصر decades ولكن بالسالب:
{ decades[it] * -1 }
كما نرى، نضع حجم القائمة بين قوسي الدالة، ثم بين قوسي اللامبدا المعقوفين، نضع التعبير الذي نريده والذي ستُمثل نتيجته عناصر القائمة المُراد إنشاؤها.
وعند تنفيذ الشفرة، تكون النتيجة:
ولضمان الفهم الصحيح لتعبير اللامبدا في دالتي ()List أو ()MutableList، دعونا ننظر إلى مثال من الواقع. بكتابة برنامج يقرأ مدخلات المستخدم لقياس درجة الحرارة السيليزية C (درجة مئوية)، وهو المقياس الرئيسي المعتمد في حياتنا اليومية في غالبية دول العالم، ثم يحوِّلها إلى الفاهرنهايت F وهو المقياس المعتمد في الولايات المتحدة الأمريكية:
في بداية الشفرة، لدينا قائمة celsius محددة الحجم بـ 5 عناصر. لذا ستعمل دالة ()readln لخمس مرات متتالية، يتمكن فيها المستخدم من إدخال عناصر القائمة. ولأن دالة ()readln تعيد كائن من النوع String، لذا نستخدم دالة ()toInt لتحويل المُدخلات إلى النوع Int حتى نستطيع التعامل معها حسابيًا.
وسيكون المُدخَل الأول للمستخدم هو العنصر الأول في القائمة، والمُدخَل الثاني هو العنصر الثاني … الخ. ومتى ما تم إدخال خمسة عناصر، ينتهي عمل دالة ()readln ويتم حفظ القائمة في المتغير celsius.
أمّا بالنسبة لقائمة fahrenheit، لإرتباطها الوثيق بقائمة celsius، وضعنا لها حجم مساوي لحجم القائمة celsius، والذي وصلنا إليه عبر استخدام الخاصيّة size. وأيضًا، نريد أن تكون عناصرها كنتيجة لتعبير اللامبدا التالي:
{ (celsius[it].toDouble() * 9 / 5) + 32 }
للحصول على درجة الحرارة بالفاهرنهايت، تقول المعادلة أنه يجب ضرب قيمة الدرجة المئوية في 9 ثم تقسيم الناتج على 5 ثم إضافة 32 للناتج.
إذًا ما نحتاجه هو قيمة عناصر celsius والتي هي قيم الدرجة المئوية المُدخلة للبرنامج. لذلك وبما أن الكلمة it تًمثِّل الفهرس في تعبير اللامبدا، يمكننا الوصول لعناصر قائمة celsius بوضعها بين قوسي الفهرسة [ ]. وللحصول على أرقام أكثر دقة من النوع Int، حوّلنا عناصر قائمة celsius إلى النوع Double عند استخدامها في المعادلة عبر دالة ()toDouble.
عند تنفيذ هذه الشفرة، سيقرأ البرنامج مدخلات المستخدم 5 مرات، ثم يتم التحويل إلى فاهرنهايت ثم طباعة القائمتين:
توفر كوتلن، إجراء العمليات التالية على التجميعات:
سنفهم كل هذه العمليات بطريقة أوضح، عند تطبيقها عبر الدوال المتوفرة لها خصيصاً، في مكتبة كوتلن القياسية.
للقيام بهذه العمليات على التجميعات، تزخر مكتبة كوتلن القياسية، بالعديد من الدوال المُلحقة بالتجميعات والتي لديها معامل من نوع بيانات الدوال، أي تستقبل دالة أخرى كقيمة. والتي سنشرحها عمليًا في الفقرات أدناه، بعد ان نلقي نظرة سريعة على أهم هذه الدوال:
لفهم عمل هذه الدوال والتفريق بينها، يمكننا وضعها جميعًا في شفرة واحدة، ورؤية نتائج عمل كل منها:
وعند تنفيذ الشفرة، نرى نتائج هذه الدوال كما هو موضح في الصورة التالية:
بالإضافة للدوال المذكورة أعلاه، هناك بعض الدوال المهمة والتي تستخدم بكثرة عند إجراء عمليات على التجميعات. من هذه الدوال، دالة ()forEach، والتي مهمتها تشبه كثيرًا حلقة التكرار for. بالإضافة للدوال: ()any و ()all و ()none، والتي ستعيد true أو false حسب نتيجة تعبير اللامبدا.
لفهم هذه الدوال الأربع وكيفية عملها، دعونا نعيد كتابة برنامج تحويل مقياس درجة الحرارة الذي كتبناه في أعلى هذا الدرس، وتضمين هذه الدوال به:
هذه المرة بعد قراءة مدخلات المستخدم وحفظها في قائمة celsius، أنشأنا قائمة fahrenheit فارغة. ثم استخدمنا دالة ()forEach للدوران على كل عناصر celsius وتحويلها للفاهرنهايت عبر المعادلة. بعد التحويل نضيف العنصر للقائمة fahrenheit عبر الدالة ()add والتي تتوفر لها لأنها قائمة من النوع القابل للتغيير MutableList. ثم نطبع قيمة العنصرين في القائمتين.
نلاحظ أننا استخدمنا الكلمة it للإشارة لعنصر في قائمة celsius، هذا لأن دالة ()forEach تم استدعاؤها عبر هذه القائمة.
في الأسطر الست الأخيرة في الشفرة، طبّقنا الدوال الثلاث: ()any و ()all و ()none. كل دالة منها تعيد true أو false. مثلًا، في السطر الأول نفحص ما إذا كان هناك عنصر في قائمة celsius أكبر من صفر، ستكون النتيجة true، لأن هناك عنصر أكبر من الصفر فعلًا. وهكذا في باقي الأسطر التالية، التي تشرح نفسها بنفسها.
وعند تنفيذ الشفرة، ستكون النتيجة:
عند استخدام دالتي ()filter و ()map مع تجميعة Set، ستعيد الدالتين تجميعة من النوع قائمة List.
هذا الدرس هو جزء من سلسلة تعليم مبادئ البرمجة بلغة كوتلن. لمُتابعة الدروس منذ البداية ومُشاهدة فهرس المحتويات يمكنك الانتقال إلى الدرس الأول من هنا.
هل أعجبك المحتوى وتريد المزيد منه يصل إلى صندوق بريدك الإلكتروني بشكلٍ دوري؟
انضم إلى قائمة من يقدّرون محتوى إكسڤار واشترك بنشرتنا البريدية.