המרה לספרות רומיות

לפעמים הבעיות הכי מעניינות הן אלה שניתן לתאר בצורה הפשוטה ביותר.
התבקשתי לתת פתרון לבעיה שניתן לתאר ב-4 מילים – המרת מספרים לספרות רומיות. ובקצת יותר מילים: כתיבת תכנית שתקבל מספר דצימלי (עשרוני) ותחזיר אותו בגרסתו הרומית. לאלה מכם שלא סגורים על איך בדיוק השיטה הרומית עובדת, כדאי להציץ כאן (גם אני הייתי זקוק לרענון לפני שהתיישבתי לכתוב את הפתרון)

יש גם חלק שני לבעיה והוא גם מאתגר יותר (אכתוב על הפתרון שלו בפוסט המשך)

הערה קטנה לפני שמתחילים, הכתב הרומי, מסתבר, תומך בכתיבה של מספרים עד המספר 3,999,999 (כנראה שלתקופה ההיא זה היה מספיק). החל מ-5000 האותיות מקבלות מן קו כזה מעליהן שבכל הכנות אני לא יודע איך להפיק עם המקלדת שלי אז אתייחס לספרות הרומיות עד M (אלף) מה שיאפשר לי להגיע עד המספר הלא כל כך מרשים 3,999. בנוסף, הכתב הרומי לא מכיל ייצוג של 0 או מספרים שליליים אז פתרתי את עצמי גם מעונשם של אלה.

בשלב הראשון נייצר מבנה נתונים כלשהו שיכיל את הספרות הרומיות והמקבילה העשרונית של כל אחת מהן. האובייקט הזה יהיה הבסיס לכל המרה שנבצע ובאופן טבעי בחרתי במילון אבל זה יכול באותה מידה גם להיות רשימה (מכילה בתוכה תתי רשימות) טאפל וכו'.
ע"פ החוקיות של שיטת הסימון הרומית, לאחר 3 חזרות אנחנו עוברים מחיבור הסימנים לחיסור: 3 לדוגמה, מיוצג ע"י חיבור של שלוש אחדות – III.
4 מיוצג ע"י החסרה של אחד מ-5 – IV.
כדי לפשט את הקוד אני אתייחס למקרים האלה (כל מספר שמתחיל ב-4 או 9) כסימנים נפרדים:

לא משנה איזה מספר נקבל, המחרוזת הרומית שעלינו להרכיב תכיל חלק מהמפתחות האלה, בסדר הפוך לזה שכתבתי כאן. למילונים בכל מקרה אין סדר מוגדר אז אצור רשימה שתכיל את המפתחות בלבד בסדר יורד.

בשלב הבא נקלוט את המספר שעלינו להמיר ונוודא שהוא בגודל המתאים (בין 1 ל-3,999 – הטווח המספרי שאני מסוגל לייצג בפורמט הרומי)

אוקי, עד כאן הכנו את הקרקע, עכשיו מגיעים לתכלס. אנו מתחילים ממחרוזת ריקה (משתנה שאקרא לו roman_number) ועוברים על כל הסכומים שהגדרנו, מגדול לקטן (הרשימה amounts כבר מכילה אותם בסדר הזה). עבור כל אחד מהסכומים (אני קורא לכל אחד מהם פשוט  amount בלולאה), נבדוק כמה פעמים הם נכנסים בשלמותם במספר שקיבלנו (המספר שקיבלנו חלקי amount). את התוצאה נעגל למספר שלם כדי להיפטר מהשארית. את המספר שקיבלנו (תמיד יהיה בין 0 ל-3) נכפיל בתו הרומי הרלוונטי (מילון במקום amount) ונוסיף למחרוזת הכללית roman_number.

הדבר היחיד שנשאר לנו לעשות זה להפחית את הסכום שהרגע הוספנו למחרוזת (roman_number) מהמספר שאנחנו בודקים (decimal_number). אם כדי לבדוק כמה תווים יש להוסיף, בדקנו ע"י חלוקה של המספר תוך התעלמות מהשארית, לאיטרציה הבאה של הלולאה נרצה לבדוק רק את מה שנשאר (השארית) שאותה נחשב בעזרת האופרטור מודולו (%) ונציב מחדש במשתנה המספרי decimal_number.

לסיום נדפיס את המספר ואולי אפילו נוסיף איזו הודעה במידה והמספר המקורי היה מחוץ לטווח שהגדרנו.

יכול להיות שנרצה להפוך את הקוד לקצת יותר שימושי אם נרצה לחזור על הפעולה יותר מפעם אחת. במקרה כזה רצוי ונכון לייצר מכל העניין פונקציה קטנה שתאפשר לנו לעשות שימוש חוזר בתכנית. הקוד יראה משהו כזה: