• בלוג
  • טיפ קלוז'ר: שימוש באוסף כפונקציה

טיפ קלוז'ר: שימוש באוסף כפונקציה

18/05/2023

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

דוגמה ראשונה היא קבוצה - בקלוז'ר אנחנו מגדירים קבוצה, כלומר HashSet, באמצעות סימן סולמית ואחריו סוגריים מסולסלים:

(def items #{1 2 3})

בשביל לגלות אם אלמנט מסוים שייך לקבוצה אני רק צריך להפעיל את הפונקציה על האלמנט:

(items 2) => 2

אותה התנהגות קיימת גם למערכים. אני מגדיר מערך עם סוגריים מרובעים:

(def items ["a" "b" "c" "d"])

וניגש לאינדקס מסוים באמצעות "הפעלת" המערך על האינדקס:

(items 2) => "c"

ואותה התנהגות קיימת גם בעבודה על מפות. אני מגדיר מפה עם:

(def items {:foo 10 :bar 20})

וניגש לאיבר עם:

(items :foo) => 10

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

לדוגמה הפונקציה remove מוחקת איברים מתוך Sequence. היא מקבלת בתור פרמטר פרדיקט - שזה פונקציה שמקבלת איבר אחד ומחזירה האם צריך למחוק את האיבר או לא - ואם הפרדיקט החזיר ערך אמת מוחקת את האיבר.

שימוש נאיבי ב remove כדי למחוק את המספרים 2, 3 ו-5 מתוך רצף עשוי להיות:

(remove #(or
          (= % 2)
          (= % 3)
          (= % 5)) (range 10)) => (0 1 4 6 7 8 9)

אבל אם אנחנו זוכרים שקבוצה היא גם פונקציה, אפשר בקלות לשכתב את הקוד ולכתוב:

(remove #{2 3 5} (range 10))

ולקבל בדיוק את אותה תוצאה בכתיב ברור וקצר יותר.