هياكل البيانات والخوارزميات / العقود والتسلسلات والبحث / الدرس 4
المكدسات والطوابير وسياسات الترتيب
اختر ترتيب الإزالة وفقا للمهمة، وأثبت أن الحاوية تحفظ العمل غير المنتهي الذي تمثله.
في هذه الصفحة
أهداف الدرس
- سياسة الترتيب هي جوهر التجريد
- طابق البنية المتداخلة باستخدام ثابت للمكدس
- استخدم الطابور للحفاظ على ترتيب الوصول
سياسة الترتيب هي جوهر التجريد
يزيل المكدس أولا أحدث عنصر أضيف ولم ينته العمل عليه، بينما يزيل الطابور أقدم عنصر أولا. تأتي فائدتهما من هاتين السياستين، وليس من اسم صنف معين. تحتاج مطابقة الأقواس المتداخلة إلى أحدث فاتح لم يُطابق، بينما يحتاج توزيع الأدوار بعدالة إلى خدمة الوافدين الأقدم قبل العمل المعاد إلى الطابور. حدّد ما يمثله كل عنصر مخزن، ومتى يدخل الحاوية ومتى يخرج منها. فالحاوية الصحيحة التي تتبع سياسة خاطئة تظل تنتج خوارزمية خاطئة. تعمل هذه الأمثلة على نصوص محلية صغيرة وسجلات وظائف مصطنعة، ولا تشغّل مجدولات أنظمة أو خدمات أو تجارب تحميل.
طابق البنية المتداخلة باستخدام ثابت للمكدس
في فحص الأقواس، يحوي المكدس فاتحات الأقواس غير المطابقة فقط، وبترتيب ورودها. ويجب أن يطابق القوس الغالق قمة المكدس، لأن البنية المتداخلة تغلق أولا أعمق مجموعة ما زالت مفتوحة. يتجاهل البرنامج الأول المحارف العادية التي ليست أقواسا، ويرفض رمز غلق مفاجئا أو غير مطابق. وعند النهاية يعني عدم فراغ المكدس أن بعض الفاتحات لم تُغلق. هذا عقد صياغي تعليمي مصغر، وليس محللا كاملا للغة برمجة؛ فهو لا يفهم النصوص المقتبسة أو محارف الهروب أو التعليقات أو قواعد اللغة. وتحتاج هذه الميزات إلى تصميم أغنى لتقسيم الرموز وتحليلها.
def balanced(text):
pairs = {")": "(", "]": "[", "}": "{"}
stack = []
for char in text:
if char in pairs.values():
stack.append(char)
elif char in pairs:
if not stack or stack.pop() != pairs[char]:
return False
return not stack
print(balanced("a([{}])"))
print(balanced("([)]"))
print(balanced(""))
True
False
True
ينفذ append وpop في طرف القائمة نفسه سياسة المكدس. ولا يحوي النص الفارغ أي أقواس غير مطابقة.
استخدم الطابور للحفاظ على ترتيب الوصول
يحاكي البرنامج الثاني عملا يتوزع دوريا، بوحدة عمل واحدة في كل دور. يضمن السحب من البداية وإلحاق العمل غير المنتهي بالنهاية أن تحصل الوظيفة المنتظرة بالفعل على دورها التالي أولا. تدعم deque عمليات طرفية فعالة، وتتجنب إزاحة عناصر قائمة Python مرارا باستعمال pop(0). تفترض المحاكاة مقادير عمل صحيحة غير سالبة، ومدخلا محدودا مغلقا لا يستقبل وظائف جديدة؛ أما المجدول الحقيقي فيحتاج إلى توقيت الوصول والأولويات والإلغاء وقواعد الموارد. يوضح الأثر ترتيب التنفيذ فقط، وليس توصية لجدولة أنظمة التشغيل أو ادعاء بعدالة عامة مع وصول وظائف اعتباطي.
from collections import deque
def turns(jobs):
queue = deque()
for name, remaining in jobs:
if not isinstance(remaining, int) or remaining < 0:
raise ValueError("Work must be a nonnegative integer")
if remaining:
queue.append((name, remaining))
trace = []
while queue:
name, remaining = queue.popleft()
trace.append(name)
remaining -= 1
if remaining:
queue.append((name, remaining))
return trace
print(turns([("A", 2), ("B", 1)]))
print(turns([("Idle", 0)]))
['A', 'B', 'A']
[]
تعمل A مرة، ثم تعمل B بينما تنتظر A، ثم تكمل A عملها. ولا تحتاج الوظائف ذات العمل الصفري إلى إدخال في الطابور.
أدرج أقصى عمل غير منته في تحليل المساحة
قد يحتاج فحص الأقواس إلى مساحة مكدس خطية إذا لم يحو النص سوى رموز فتح. ويمكن لمحاكاة الطابور، وفقا لهذا العقد، أن تحتفظ بإدخال واحد على الأكثر لكل وظيفة مدخلة لم تنته بعد؛ أما الأثر المعاد فيحوي عنصرا لكل وحدة عمل. وقد يكون الناتج أكبر كثيرا من عدد الوظائف، ولذلك حلل إجمالي العمل W مستقلا عن عدد الوظائف n. تكون كلفة المحاكاة O(n+W) مع افتراض عمليات محدودة الكلفة، ويحتاج حفظ الأثر كله إلى مساحة ناتج O(W). ويمكن لمستهلك تدفقي تجنب الاحتفاظ بالأثر إذا سمح عقد النتيجة بذلك.
عالج الإزالة من الفراغ والسعة المحدودة عن قصد
ينبغي أن يمنع ثابت الخوارزمية السحب من مكدس أو طابور فارغ، أو أن يُعرض ذلك فشلا موثقا. لا تلتقط كل استثناء ثم تتظاهر بصمت بأن عنصرا كان موجودا. قد تسقط deque محدودة بـ maxlen إدخالات عند وصول عناصر جديدة؛ وقد يفيد ذلك في مخزن لأحدث الأحداث، لكنه غير صحيح لطابور عمل لم ينته. سياسة السعة جزء من صحة الحل. فإذا وجب أن تعالج كل وظيفة مدرجة في النهاية، فإن الطرد الصامت يخالف العقد، حتى لو اتبعت الحاوية سلوك واجهتها الموثق تماما.
تحقّق من الترتيب وحفظ العمل معا
لمطابق الأقواس بالمكدس، اختبر غالقات بلا فاتحات، وفاتحات بلا غالقات، وتداخلا خاطئا وآخر صحيحا، والنص الفارغ، والمحارف العادية المتجاهلة. وللطابور تحقّق من مساواة عدد مرات كل وظيفة في الأثر لمقدار العمل المطلوب لها، ومن سبق الوظائف المنتظرة أصلا للوظيفة الأولى عندما تعاد إلى الطابور. أدرج العمل الصفري والعمل السالب غير الصالح. تكشف فحوص حفظ العمل الوظائف الضائعة أو المكررة التي قد لا تكشفها بداية أثر تبدو مقنعة. تبقى الاختبارات محددة وصغيرة؛ فلا تحتاج هذه القواعد المحلية إلى خيوط متزامنة أو مجدولات حقيقية أو حمل قياس معياري.
الأخطاء الشائعة وإصلاحها
استخدام pop() لطابور، فينعكس ترتيب الانتظار
- لماذا يحدث؟
- اتبعت الإزالة سياسة المكدس بدلا من FIFO، أي الأسبق دخولا أسبق خروجا.
- كيف تصلحه؟
- أزل العنصر من البداية باستخدام تنفيذ مناسب للطابور.
- كيف تتجنب تكراره؟
- اختبر ثلاثة وافدين أوليين مختلفين.
نجاح تداخل خاطئ في فحص الأقواس
- لماذا يحدث؟
- عدت الخوارزمية الرموز، لكنها تجاهلت ترتيبها.
- كيف تصلحه؟
- طابق كل رمز غلق مع الرمز الموجود في قمة المكدس.
- كيف تتجنب تكراره؟
- أدرج النص ([)] مثالا مضادا.
فقد طابور محدود وظائف غير منتهية بصمت
- لماذا يحدث؟
- استُخدمت سياسة طرد مخزن السجل لعمل يجب تنفيذه.
- كيف تصلحه؟
- اختر رفضا صريحا عند بلوغ السعة، أو طابورا مصطنعا غير محدود السعة.
- كيف تتجنب تكراره؟
- تحقّق من حفظ عدد وحدات العمل لكل وظيفة.
اختبر فهمك
1. لماذا لا يكفي تساوي عدد أقواس الفتح وعدد أقواس الغلق؟
اعرض الحل المشروح
تتساوى الأعداد في النص )(، لكنه يغلق قبل وجود أي فاتح. ومع أنواع أقواس متعددة، تتطابق المجاميع في ([)] أيضا، لكن التداخل خاطئ. يحفظ ثابت المكدس وجود الفاتح وترتيب إغلاق الأعمق معا، بينما يفقد العد وحده هاتين المعلومتين.
2. ما الطول الكلي للأثر عندما تكون مقادير العمل 2 و0 و3؟
اعرض الحل المشروح
يساوي 2+0+3=5، بصرف النظر عن سياسة تداخل الأدوار، ما دام لا يضيع عمل ولا يتكرر. وفي الطابور المعروض لا ينشئ العمل الصفري أي إدخال. ويوضح ذلك أهمية إجمالي العمل W إلى جانب عدد الوظائف n.
أهم ما تعلمته
- سياسة الترتيب هي جوهر التجريد
- تحقّق من الترتيب وحفظ العمل معا