المسارات
/
Python
Python
/
التمارين
/
القائمة المترابطة البسيطة
القائمة المترابطة البسيطة

القائمة المترابطة البسيطة

سهل

مقدمة

أنت تعمل في شركة لبث الموسيقى.

أوكلت إليك مهمة إنشاء ميزة قوائم التشغيل في تطبيق مشغل الموسيقى الخاص بك.

التعليمات

اكتب نموذجًا أوليًا لتطبيق مشغّل الموسيقى.

في هذا النموذج الأولي، تُمثَّل كل أغنية برقم ببساطة. بمعطى نطاق من الأرقام (معرّفات الأغاني)، أنشئ قائمة مرتبطة أحادية الاتجاه.

وبمعطى قائمة مرتبطة أحادية الاتجاه، ينبغي أن تكون قادرًا على عكس ترتيب القائمة لتشغيل الأغاني بالترتيب المعاكس.

Note

القائمة المرتبطة بنية بيانات أساسية في علم الحاسوب، وكثيرًا ما تُستخدم في تنفيذ بنى بيانات أخرى.

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

إذا أردت التعمق أكثر في القوائم المرتبطة، فاطّلع على هذا المقال الذي يشرحها برسومات جميلة.

كيف يُبنى هذا التمرين في Python

بينما يمكن تنفيذ stacks وqueues باستخدام lists وcollections.deque وqueue.LifoQueue وmultiprocessing.Queue، فإن هذا التمرين يتوقع مكدس «الأخير دخولًا، الأول خروجًا» (LIFO) باستخدام قائمة مترابطة مفردة مصنوعة يدويًا:


رسم توضيحي لمكدس منفَّذ بقائمة مترابطة. على أقصى الجانب الأيسر دائرة بحدود متقطعة اسمها New_Node، يتجه منها خطان منقطان بسهمين نحو اليمين. يحمل New_Node النص "(becomes head) - New_Node - next = node_6". ويحمل الخط المنقط العلوي وسم "push" ويشير إلى Node_6، أعلى وإلى اليمين. ويحمل Node_6 النص "(current) head - Node_6 - next = node_5". ويحمل الخط المنقط السفلي وسم "pop" ويشير إلى مربع يحمل النص "gets removed on pop()". من Node_6 سهم متصل يشير نحو اليمين إلى Node_5، الذي يحمل النص "Node_5 - next = node_4". ومن Node_5 سهم متصل يشير نحو اليمين إلى Node_4، الذي يحمل النص "Node_4 - next = node_3". ويتكرر هذا النمط حتى Node_1، الذي يحمل النص "(current) tail - Node_1 - next = None". ومن Node_1 سهم منقط يشير نحو اليمين إلى عقدة تقول "None".


لا ينبغي الخلط بين هذا وبين مكدس LIFO يستخدم مصفوفة أو قائمة ديناميكية، والذي قد يستخدم list أو queue أو array في الأسفل. المكدسات المبنية على مصفوفة ديناميكية لها موضع head مختلف، ولها تعقيد زمني (Big-O) وبصمة ذاكرة مختلفان.


رسم توضيحي لمكدس منفَّذ بمصفوفة أو مصفوفة ديناميكية. على أقصى الجانب الأيمن مربع بحدود متقطعة اسمه New_Node، يتجه منه خطان منقطان بسهمين نحو اليسار. يحمل New_Node النص "(becomes head) -  New_Node". ويحمل الخط المنقط العلوي وسم "append" ويشير إلى Node_6، أعلى وإلى اليسار. ويحمل Node_6 النص "(current) head - Node_6". ويحمل الخط المنقط السفلي وسم "pop" ويشير إلى مربع بحدود منقطة يحمل النص "gets removed on pop()". من Node_6 سهم متصل يشير نحو اليسار إلى Node_5. ومن Node_5 سهم متصل يشير نحو اليسار إلى Node_4. ويتكرر هذا النمط حتى Node_1، الذي يحمل النص "(current) tail - Node_1".


راجع هذين السؤالين على Stack Overflow لبعض الاعتبارات: المكدسات والطوابير القائمة على المصفوفات مقابل القائمة على القوائم والفرق بين مكدس المصفوفة والمكدس المترابط والمكدس. لمزيد من التفاصيل عن القوائم المترابطة ومكدسات LIFO وغيرها من أنواع البيانات المجردة (ADT) في Python:


الأصناف في Python

يتطلب التنفيذ «القياسي» لقائمة مترابطة في Python عادةً صنفًا واحدًا أو أكثر (classes). ولمقدمة جيدة عن classes، راجع classes والتمرين المرافق ellens-alien-game، أو قسم الأصناف في درس Python الرسمي.


الطرق الخاصة في Python

ستستدعي اختبارات هذا التمرين الدالة len() على LinkedList الخاص بك. ولكي تعمل len()، ستحتاج إلى إنشاء طريقة خاصة باسم __len__. لتفاصيل تنفيذ الطرق الخاصة أو طرق «dunder» في Python، راجع توثيق Python: التخصيص الأساسي للكائنات وتوثيق Python: object.len(self).


بناء مُكرِّر

لدعم المرور عبر LinkedList الخاص بك أو عكسه، ستحتاج إلى تنفيذ الطريقة الخاصة __iter__. راجع تنفيذ مُكرِّر لصنف لتفاصيل التنفيذ.


تخصيص الاستثناءات ورفعها

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

يمكن إنشاء الاستثناءات المخصصة عبر أصناف استثناءات جديدة (راجع classes لمزيد من التفاصيل) تكون عادةً أصنافًا فرعية من Exception.

وفي الحالات التي تعرف فيها أن مصدر الخطأ سيكون مشتقًا من نوع استثناء معين، يمكنك أن تختار الوراثة من أحد built in error types ضمن صنف Exception. وعند رفع الخطأ، يجب أن تُضمّن رسالة ذات معنى.

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

لتخصيص استثناء عام، أنشئ class يرث من Exception. وعند رفع الاستثناء المخصص مع رسالة، اكتب الرسالة كوسيط للنوع exception:

# subclassing Exception to create EmptyListException
class EmptyListException(Exception):
    """Exception raised when the linked list is empty.

    message: explanation of the error.

    """
    def __init__(self, message):
        self.message = message

# raising an EmptyListException
raise EmptyListException("The list is empty.")
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة
Python Exercism

مستعد لبدء القائمة المترابطة البسيطة؟

سجّل في Exercism لتتعلّم وتتقن Python عبر 17 مفهومًا146 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.