עשר דקות עם סקאלה

02/10/2023

אתם כבר יודעים שהתחלתי לא מזמן ללמוד סקאלה, ואחד הצעדים הראשונים שלי עם שפת תכנות הוא לממש דברים שאני כבר יודע לפתור רק בשביל לראות איך זה עובד. אז הנה פיתרון של Advent Of Code 2022 Day 2 בסקאלה, תוך שימוש נרחב ב match/case שלהם והחצים שנראה שמאפיינים את השפה.

1. התרגיל

בתרגיל אנחנו מקבלים קובץ שנראה בערך כך:

A Y
B X
C Z

כשהעמודה השמאלית מייצגת מהלך במשחק "אבן נייר ומספריים" (A לאבן, B לנייר ו C למספריים) והעמודה הימנית בעלת משמעות משתנה. בחלק הראשון היא מייצגת את המהלך של היריב ובחלק השני היא מייצגת את התוצאה הרצויה (Y תיקו, X העמודה השמאלית מנצחת, Z העמודה השמאלית מפסידה). כל שורה מתורגמת לנקודות לפי ניצחון, תיקו או הפסד של העמודה הימנית ולפי המהלך שבוצע בעמודה הימנית. האתגר הוא לסכום את הנקודות.

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

נקודת ההתחלה שלי היתה הפונקציה score שמקבלת שורה ומחזירה את הניקוד שלה:

def score(line: String): Int =
  line match
    case "A X" => 4
    case "A Y" => 8
    case "A Z" => 3
    case "B X" => 1
    case "B Y" => 5
    case "B Z" => 9
    case "C X" => 7
    case "C Y" => 2
    case "C Z" => 6

בסקאלה פונקציה מוגדרת עם def, לפרמטרים יש טיפוס ולפונקציה יש טיפוס החזר. אחרי הפקודה match יש רשימה של case-ים כשהם קבועים זה עובד כמו switch/case רגיל (וכן אפשר לעשות pattern matching מתוחכם כמו בשפות פונקציונאליות).

בחלק הראשון של התרגיל צריך רק לקרוא מקובץ ולסכום את התוצאה של score על כל שורה:

@main def main(): Unit =
  val filename = "./src/main/scala/day2.txt"
  var score1 = for (line <- Source.fromFile(filename).getLines)
    yield score(line)

  println(score1.sum)

את פקודת for בסקאלה אני קורא כמו List Comprehension של פייתון או for של אליקסיר, כשה yield קובע מה יוחזר. סך הכל המשתנה score1 יהיה רשימה של תוצאות של הפונקציה. וכן אפשר גם לוותר על כל תחביר הרכבת הרשימות הזה ולכתוב פשוט:

var score1 = Source.fromFile(filename).getLines.map(score)

אבל זה פחות כיף.

בחלק השני אני מריץ עוד לולאה הפעם עם match/case בתוך ה for כדי לקבל את רשימת הנקודות:

  var score2 = for (line <- Source.fromFile(filename).getLines)
    yield line match
      case "A X" => score("A Z")
      case "A Y" => score("A X")
      case "A Z" => score("A Y")
      case "B X" => score("B X")
      case "B Y" => score("B Y")
      case "B Z" => score("B Z")
      case "C X" => score("C Y")
      case "C Y" => score("C Z")
      case "C Z" => score("C X")


  println(score2.sum)

3. רשמים על השפה בינתיים

כמה דברים שלמדתי בינתיים:

  1. בסקאלה אינדנטציה חשובה, כמו בפייתון.

  2. מערכת הטיפוסים יכולה לעזור, אבל גם יכולה להיות מרגיזה ולהראות הודעות שגיאה לא ברורות.

  3. קהילה קטנה אומרת שאפשר למצוא הרבה פחות תיעוד, וגם דוגמאות של סקאלה 2 לפעמים יהיו שונות מסקאלה 3.

  4. ידע ב Java מאוד עוזר להבין את סקאלה.

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