cb
codebamin
الفصل 12 · القواميس (Dictionaries)

الاستخدام العملي للقواميس

لنستخدم القواميس في مشكلة واقعية. تخيل أنك تبني متجرًا إلكترونيًا، وتحفظ أسعار المنتجات في قاموس:

prices = {'apple': 2, 'box': 5, 'cat': 100, 'dog': 100}

هذا يوضح سبب كون البحث في القاموس قائمًا على المفاتيح لا القيم. ماذا يمكن أن تعني prices[100]؟ هل النتيجة 'dog' أم 'cat' أم الاثنتان؟ يمكن أن تتكرر القيمة في القاموس مرات كثيرة.

أما المفاتيح فيجب أن تكون فريدة. تخيل مثلًا:

prices = {'apple': 2, 'apple': 3}

كم سعر التفاحة؟ نعرف أننا سنكتب prices['apple']، لكن هل القيمة 2 أم 3؟ منطقيًا يجب أن يكون هناك سعر واحد تحت المفتاح نفسه، ولذلك لا يمكن الاعتماد على مفاتيح مكررة لتمثيل قيمتين مختلفتين.

لدينا الآن متجر عادي، وعملاء لديهم قوائم مشتريات مثل ['apple', 'box', 'cat']. نريد حساب السعر الإجمالي بأنفسنا.

أكمل الدالة التالية، وخصوصًا السطر price = ...:

__copyable__ def total_cost(cart, prices): result = 0 for item in cart: price = ... result += price return result

check_result( total_cost( ['apple', 'box', 'cat'], {'apple': 2, 'box': 5, 'cat': 100, 'dog': 100}, ), 107, )

تذكّر أن prices قاموس، وأن العناصر الموجودة في cart هي المفاتيح التي تحتاجها للحصول على الأسعار.

ممتاز! نشرت المتجر وبدأت تتخيل الأرباح، ثم جاء عميل يريد شراء خمسة ملايين كلب... وصندوقين ليضعها فيهما.

الموقع يستطيع تمثيل شراء العنصر نفسه عدة مرات بتكراره داخل cart، مثل ['box', 'box']، لكن هذا غير عملي عندما تكون الكمية ضخمة. لذلك سنفصل نوع العنصر عن الكمية.

هذا الكود الجديد يحتاج إلى إكمال:

__copyable__ def total_cost(cart, quantities, prices): result = 0 for item in cart: price = ... quantity = ... result += price * quantity return result

check_result( total_cost( ['dog', 'box'], {'dog': 5000000, 'box': 2}, {'apple': 2, 'box': 5, 'cat': 100, 'dog': 100}, ), 500000010, )

أضفنا معاملًا اسمه quantities. ما زالت cart قائمة من النصوص، لكن من دون تكرار. أما quantities فهو قاموس مفاتيحه هي العناصر الموجودة في cart، وقيمة كل مفتاح هي الكمية المطلوبة من ذلك العنصر.

استخدم المفتاح نفسه item للوصول إلى السعر في prices والكمية في quantities.

جيد! لكن ربما لاحظت شيئًا غير مريح: لماذا نكتب 'dog' و'box' في cart ثم نكررهما كمفاتيح في quantities؟ في الجزء التالي سنتعلم المرور مباشرة على مفاتيح القاموس حتى نستطيع الاستغناء عن cart.

قبل ذلك، لنتدرب أكثر.

تعاملنا سابقًا مع تحويل سلسلة DNA إلى سلسلة مقابلة بتبديل كل نوكليوتيد بما يطابقه. هذه نسخة من الفكرة داخل دالة؛ تستبدل كل حرف في string بحرف آخر:

__copyable__ __program_indented__

يمكننا الآن استخدام قاموس لجعل الكود السابق أقصر وأكثر عمومية.

أضف إلى الدالة substitute معاملًا ثانيًا اسمه d وهو قاموس. مفاتيح d تمثل الأحرف التي قد تظهر في string، وقيمة كل مفتاح هي الحرف الذي يجب أن يحل مكانه. مثلًا الزوج 'A': 'T' يعني استبدال 'A' بـ'T':

__copyable__ def substitute(string, d): ...

original = 'AGTAGCGTCCTTAGTTACAGGATGGCTTAT' expected = 'TCATCGCAGGAATCAATGTCCTACCGAATA' check_result(substitute(original, {'A': 'T', 'T': 'A', 'C': 'G', 'G': 'C'}), expected)

يجب أن تعمل هذه النسخة لأي مدخلات تكون فيها كل أحرف string مفاتيح موجودة في d. لم تعد هناك حاجة إلى سلسلة طويلة من if وelif: أثناء المرور على أحرف النص، استخدم الحرف نفسه كمفتاح للحصول على البديل المناسب من القاموس وابنِ النص الناتج.

رائع! يمكن استخدام الدالة نفسها أيضًا لتشفير رسائل بسيطة وفك تشفيرها:

__copyable__ __no_auto_translate__ def substitute(string, d): result = "" for letter in string: result += d[letter] return result

plaintext = 'helloworld' encrypted = 'qpeefifmez' letters = {'h': 'q', 'e': 'p', 'l': 'e', 'o': 'f', 'w': 'i', 'r': 'm', 'd': 'z'} reverse = {'q': 'h', 'p': 'e', 'e': 'l', 'f': 'o', 'i': 'w', 'm': 'r', 'z': 'd'} check_result(substitute(plaintext, letters), encrypted) check_result(substitute(encrypted, reverse), plaintext)

الدالة نفسها تعمل في الاتجاهين؛ كل ما نفعله هو تمرير قاموس مختلف.

القاموسان متشابهان جدًا، لكننا نعكس المفتاح والقيمة في كل زوج. عند التشفير نستبدل e بـp، وعند فك التشفير نعيد p إلى e.

لاحظ أن 'e' تظهر كمفتاح وكقيمة داخل letters.

عندما نكتب letters['e'] فنحن نسأل عن 'e' بصفتها مفتاحًا، ولذلك نحصل على 'p'. ولا نستطيع سؤال القاموس نفسه مباشرة عن المفتاح الذي ترتبط به القيمة 'e'. لكن في هذا المثال لدينا القاموس العكسي؛ فـreverse['e'] تعطي 'l'، بينما letters['l'] تعيد 'e' مرة أخرى.

قريبًا ستكتب دالة تنشئ القاموس العكسي تلقائيًا، أي شيئًا مثل:

reverse = swap_keys_values(letters)

solution.py
1
2
3
4
5
6
7
8
9
10
الطرفية (shell)
>>>