פיתרון פרויקט אוילר שאלה 12 בסקאלה

29/11/2023

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

1. התרגיל

ניקח את סידרת הסכומים החלקיים של המספרים הטבעיים כלומר כל איבר הוא סכום כל המספרים הטבעיים עד אליו:

1, 3, 6, 10, 15, 21, 28

מצאו את המספר הראשון בסידרה זו שמתחלק ב 500 מספרים או יותר.

2. פיתרון בסקאלה

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

def divisorsCount(n: Int): Int =
  (1 to sqrt(n).toInt)
    .filter(n % _ == 0)
    .flatMap(i => List(i, n / i))
    .toSet[Int]
    .size

אחרי שזה מוכן אפשר להשתמש בו כדי לפתור את התרגיל:

def euler12(): Int =
  LazyList
    .from(0)
    .scanLeft(0)(_ + _)
    .dropWhile(divisorsCount(_) < 500)
    .head

3. מה למדתי על סקאלה

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

.dropWhile(divisorsCount(_) < 500)

כי זה ברור שאני עובד על הפרמטר מהפקודה הקודמת.

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