בעיה ט"ו

הוראות המופיעות בגוף של מבנה while ומתבצעות כל עוד תנאי הלולאה מתקיים צריכות להיות מוזזות ימינה ביחס לשורה הראשונה במבנה. הקוד התקין:
age = input(‘Insert age; -1 to stop: ‘)
while age != ‘-1’:
print(age)
age = input(‘Insert age; -1 to stop: ‘)
כתבו את הפונקציה pricesDiffs –
def pricesDiffs(dicts):
לפונקציה פרמטר אחד: dicts – רשימה ובה שני מילונים או יותר. כל מילון ברשימה שומר מידע בנוגע למוצרים שנמכרו בבוטיק שוקולד ביום אחד מסוים. המפתחות במילון הם קודים (מחרוזות) של מוצרים והערכים הם מחירי המוצרים (מספרים שלמים). אפשר להניח שיש ברשימה dicts מילונים עבור כל אחד ואחד מהימים שבהם פעל הבוטיק מאז פתיחתו, כולל יום הפתיחה והיום, וכי הרשימה ממוינת בסדר כרונולוגי עולה: המילון הראשון בה (באינדקס 0) מכיל מידע בנוגע ליום הפתיחה, והמילון האחרון בה מכיל מידע בנוגע להיום. הנה דוגמה לרשימה dicts –
[{“AB12”: 5, “CD76”: 3, “BY61”: 6},
{“AB12”: 7, “DA81”: 8, “BY61”: 2},
{“UX17”: 12, “AB12”: 11, “BY61”: 2}]
לפי רשימת דוגמה זו, יום הפתיחה של הבוטיק היה שלשום. בנוסף ביום הפתיחה מחיר המוצר שהקוד שלו הוא AB12″” היה 5.
הפונקציה תכין מילון חדש. במילון זה המפתחות הם הקודים של כל המוצרים שנמכרו בבוטיק בכל ימי פעילותו עד היום (כולל). כל מפתח במילון החדש, כלומר כל קוד מוצר, ימופה למספר שהוא מחיר המוצר היום פחות מחיר המוצר ביום פתיחת הבוטיק (ההפרש הזה יכול להיות שלילי אם המחיר היום קטן מהמחיר ביום פתיחת הבוטיק). למשל עבור מילון הדוגמה הפונקציה תיצור את המילון הזה –
{‘AB12’: 6, ‘BY61’: -4}
רק שני מוצרים, אלו שהקודים שלהם הם ‘AB12’ ו-‘BY61’, נמכרו בכל ימי פעילותו של הבוטיק, ועבור כל אחד מהם המילון שומר מידע על אודות ההפרש בין מחיר המוצר היום לבין מחירו ביום פתיחת הבוטיק.
הפונקציה תחזיר את המילון שיצרה.
פתרון
נפרק את פתרון הבעיה לשני שלבים אלו –
• בשלב הראשון נאתר את הקודים של כל המוצרים שנמכרו בבוטיק מיום פתיחתו ועד היום, ונכין אוסף המכיל את כל הקודים האלה.
• בשלב השני נשתמש באוסף שיצרנו בשלב הראשון כדי ליצור את המילון החדש לפי המתואר בבעיה.
הבעיה שיש לפתור בשלב הראשון דומה לבעיה שפגשנו בסוף הפתרון לבעיה ח’. הבעיה ההיא נוסחה כך: נתונים שני אוספים או יותר, ויש ליצור אוסף אחד המכיל את כל הערכים השונים זה מזה בכל האוספים; ערך יופיע בקבוצה החדשה גם אם אינו מופיע בכל האוספים הנתונים. בבעיה כאן התנאי להכללת הערכים באוסף הסופי הוא אחר. הנה הניסוח הכללי של הבעיה כאן:
נתונים שני אוספים או יותר. יש ליצור אוסף אחד המכיל את כל הערכים המופיעים בכל אחד ואחד מהאוספים הנתונים.
אנו יודעים היטב לפתור מקרה פרטי של הבעיה הזאת –
נתונים שני אוספים. יש ליצור אוסף אחד המכיל את כל הערכים הנמצאים בשניהם.
כמובן פתרון המקרה הזה וא אחד השימושים העיקריים של קבוצה (set). עיינו בקוד הזה –
dicts = [{“AB12”: 5, “CD76”: 3, “BY61”: 6},
{“AB12”: 7, “DA81”: 8, “BY61”: 2},
{“UX17”: 12, “AB12”: 11, “BY61”: 2}]
common = set(dicts[0].keys()) & set(dicts[1].keys())
print(common)
>>>
{‘BY61’, ‘AB12’}
dicts[0] הוא המילון הראשון ברשימה dicts, ו-dicts[0].keys() הוא אוסף המפתחות של המילון הזה. dicts[1].keys() הוא אוסף המפתחות של המילון השני ברשימה (זכרו כי לפי הבעיה יש ברשימה dicts שני מילונים או יותר). הקוד הופך את שני אוספי המפתחות לקבוצות ובאמצעות האופרטור & יוצר קבוצה המכילה את המפתחות המופיעים בשני האוספים.
מה נוגע למצב שבו ברשימה dicts יש שלושה מילונים ויש למצוא את אוסף המפתחות המשותפים לשלושתם? עיינו בקוד הזה –
dicts = [{“AB12”: 5, “CD76”: 3, “BY61”: 6},
{“AB12”: 7, “DA81”: 8, “BY61”: 2},
{“UX17”: 12, “AB12”: 11, “BY61”: 2}]
common1 = set(dicts[0].keys()) & set(dicts[1].keys())
common2 = common1 & set(dicts[2].keys())
print(common2)
>>>
{‘BY61’, ‘AB12’}
ההוראה השניה בקוד זה מפיקה את הקבוצה שהפיק קטע הקוד הקודם: קבוצה המכילה את כל המפתחות במילון הראשון ובמילון השני ברשימה dict. ההוראה השלישית בקוד הנוכחי לוקחת את הקבוצה הזאת ויוצרת קבוצה המכילה את כל המפתחות הנמצאים הן בה הן במילון השלישי ברשימה, כלומר קבוצת המפתחות המופיעים בכל שלושת המילונים האלה.
הטיפול במקרים הפרטיים האלה מורה לנו על האלגוריתם שעל פיו נפתור את המקרה הכללי של הבעיה, כלומר מציאת כל המפתחות שיש בכל אוספי המפתחות במספר כלשהו של מילונים –
(1) נגדיר משתנה בשם alwaysSold – הוא יכיל קבוצה (set) של המפתחות במילון הראשון ברשימה dicts
(2) נסרוק את שאר המילונים ברשימה, ועבור כל מילון –
(2.1) ניצור קבוצה המכילה את כל המפתחות הנמצאים הן במילון הנסרק הנוכחי
הן בקבוצת המפתחות הנמצאים כרגע במשתנה alwaysSold,
ונציב את הקבוצה החדשה במשתנה alwaysSold
הנה הצעה למימוש אלגוריתם זה –
alwaysSold = set(dicts[0].keys())
for d in dicts[1:]:
alwaysSold = alwaysSold & set(d.keys())
האוסף הנסרק בלולאת ה-for הוא כל המילונים ברשימה dicts חוץ מהמילון הראשון בה, כלומר זה שקבוצת המפתחות בו הוצבה במשתנה alwyasSold לפני הלולאה. שוב נזכיר כי לפי הבעיה ידוע כי ברשימה dicts יש לפחות שני מילונים.
סיימנו את המימוש של השלב הראשון של כתיבת הפתרון: יש בידינו קוד היוצר אוסף של כל המפתחות המשותפים לכל המילונים ברשימה. נפנה עתה ליצירת המילון שהפונקציה מחזירה. לפי המוסבר בבעיה, כל מפתח במילון זה הוא קוד בקבוצה שיצרנו בשלב הקודם, זו המוצבת במשתנה alwaysSold, וכל ערך הוא ההפרש בין המחיר של המוצר היום למחירו ביום פתיחת הבוטיק. שוב עלינו ליצור אוסף בלולאה לפי התבנית המוכרת: אתחול האוסף לאוסף ריק – כאן: מילון ריק – ואחר כך הרצת לולאה והוספת ערך לאוסף בכל סיבוב שלה (ראו לעיל בעיה א’). הנה הצעה למימוש שלב זה –
diffs = {}
for product in alwaysSold:
diffs[product] = dicts[-1][product] – dicts[0][product]
עד לסוף הלולאה יכיל המשתנה diffs את המילון המבוקש. לפניה הוא מאותחל למילון ריק. הלולאה סורקת כל קוד וקוד בקבוצה alwyasSold, ובכל סיבוב מוסיפה למילון זוג שהמפתח בו הוא הקוד הנסרק, והערך בו הוא הפרש מחירים: המחיר של המוצר לפי המילון הנמצא באינדקס 1- (מינוס 1) ברשימה dicts, כלומר המילון הנמצא בסוף הרשימה ומכיל מידע על אודות המוצרים הנמכרים בבוטיק היום, פחות המחיר של המוצר לפי המילון הנמצא באינדקס 0 ברשימה dicts, כלומר המילון הנמצא בראש הרשימה ומכיל מידע על אודות המוצרים שנמכרו בבוטיק ביומו הראשון. תנו דעתכם להפעלה הכפולה של האופרטור [ ] – ההפעלה השמאלית היא על רשימה (dicts) ומחזירה מילון, וההפעלה הימנית היא על המילון המוחזר מההפעלה הראשונה, ומחזירה את הערך שאליו ממופה קוד המוצר הנכתב בתוך הסוגריים.
הנה הקוד המלא של הפונקציה –
[3, ‘x’, ‘y’, [5, ‘ABBA’], 6.3, ‘z’, (8, 3, 5)]
רשימה זו נבדלת מרשימת הקלט הקודמת בערך יחיד: באינדקס 2 מופיעה בה המחרוזת ‘y’ ולא המספר 17-. כלומר כאן עלינו להסיר שני ערכים רצופים. הנה הפלט שיתקבל מזימון הפונקציה על רשימה זו –
def pricesDiffs(dicts):
alwaysSold = set(dicts[0].keys())
for d in dicts[1:]:
alwaysSold = alwaysSold & set(d.keys())
diffs = {}
for product in alwaysSold:
diffs[product] = dicts[-1][product] – dicts[0][product]
return diffs
את הקוד המממש את השלב השני, יצירת המילון diffs (מסומן כאן בגופן עבה), נוכל לקצר באמצעות ביטוי Dict Comprehension, ולכתוב את הפונקציה כך –
def pricesDiffs(dicts):
alwaysSold = set(dicts[0].keys())
for d in dicts[1:]:
alwaysSold = alwaysSold & set(d.keys())
return {product : dicts[-1][product] – dicts[0][product] for product in alwaysSold}
שלא כקודמתה גרסה זו של הפונקציה אינה יוצרת משתנה נפרד (diffs) המכיל את המילון הנוצר לפני החזרתו.