היפוך סדר המילים במשפט בגירסא פונקציונאלית
אני יודע שאני קצת חופר לכם על תכנות פונקציונאלי בימים האחרונים אבל מקווה שזה לטובה. כי העניין עם תכנות פונקציונאלי (או כל פרדיגמה אחרת שאתם לומדים) הוא שאנחנו תמיד מחפשים לחשוב איך זה מתקשר לדברים שאנחנו כבר יודעים. איך אפשר לכתוב את הקוד שרציתי לכתוב באליקסיר או ליספ, ואז שואלים את השאלות הלא נכונות.
אני מקווה שהדוגמאות הקטנות האלה מדי פעם יעזרו לכם להבין את צורת המחשבה והגישה, ואת הרעיון שכשאתם באים לכתוב קוד פונקציונאלי המטרה שלכם היא לחשוב קודם כל מה הדרך הפונקציונאלית לכתוב את התוכנית ורק אחר כך לגשת לכתוב את הקוד.
אז הנה דוגמא קצרה להיום על היפוך סדר המילים במשפט. כך נראה קוד JavaScript שמקבל משפט כפרמטר בשורת הפקודה ומדפיס את אותו משפט בסדר מילים הפוך:
sentence = process.argv[2];
result = [];
words = sentence.split(/\W+/);
for (let i=words.length - 1; i >= 0; i--) {
result.push(words[i]);
}
console.log(result.join(" "));
אבל המעבר לתכנות פונקציונאלי משנה את התוכנית לחלוטין - ולא רק בגלל שפת התכנות. הנה אותו הקוד באליקסיר:
System.argv
|> List.first
|> String.split
|> Enum.reverse
|> Enum.join(" ")
|> IO.inspect
זה נראה קצת לא קריא במבט ראשון אז בואו נסתכל על זה יחד ואחרי זה נחזור לכתוב את הגירסא הפונקציונאלית של JavaScript. מסתבר שבאליקסיר אין לולאות for ואי אפשר להוסיף אלמנטים למערכים כי כל מבני הנתונים בשפה הם Immutable. מגבלה זו מכריחה אותנו לכתוב קוד די שונה.
הדבר השני שיש באליקסיר הוא אופרטור ה Pipeline (שאולי יגיע בקרוב גם ל JavaScript). בגלל שאנחנו מפעילים המון פונקציות הם יצרו קיצור ובמקום לכתוב:
List.first(System.argv)
אפשר לכתוב את הדבר הזה שעושה בדיוק את אותו דבר:
System.argv |> List.first
כל שורה לוקחת את התוצאה של השורה שלפניה ופועלת עליה כלומר מעבירה את התוצאה של הפקודה הקודמת בתור פרמטר ראשון לפונקציה.
עכשיו בחזרה לקוד: הגירסא הפונקציונאלית צריכה לתאר איזה פעולות עושים על המידע ובאמצעות הרכבת פעולות מגיעה לתוצאה כמעט בלי חישובי ביניים. במקרה שלנו יש 5 פעולות בסיסיות:
- לוקחים את המשתנה הראשון שקיבלנו בשורת הפקודה.
- את התוצאה שוברים למילים.
- את התוצאה הופכים.
- את התוצאה מחברים חזרה.
- את התוצאה מדפיסים.
החשיבה הפונקציונאלית לא מבוססת על לולאות ושינויי משתנים אלא על פעולות ומידע. כשאתם יכולים לנסח התנהגות בצורה של הרכבה של פעולות בסיסיות אתם מתקרבים לצורת החשיבה הנכונה. ואיך מתרגמים את זה בחזרה ל JavaScript? די בקלות:
console.log(
process.argv[2]
.split(/\W+/)
.reverse()
.join(" ")
)