תכנות פונקציונלי
במחשבים, תכנות פונקציונלי היא פרדיגמת תכנות השמה דגש על חישוב ביטוי תוך שימוש בפונקציות ככלי ההפשטה העיקריים. זאת בניגוד לפקודות (Statements) שהן הכלי העיקרי של שפות התכנות מהפרדיגמה הנפוצה יותר, הפרדיגמה האימפרטיבית.
על השפות הפונקציונליות נמנות שפות כגון LISP הוותיקה, Haskell, ML ו-Scheme.
הפרדיגמה הפונקציונלית לא נכנסה אל הזרם המרכזי בעולם התוכנה, אך שפות מודרניות רבות הנמצאות בשימוש מסיבי בתעשייה, אף שאינן שפות פונקציונלית, מאפשרות מאפיינים פונקציונליים רבים: פייתון ו-#C הן שתי דוגמאות בולטות.
תוכן עניינים |
רקע תאורטי [עריכה]
המבנה התאורטי עליו מתבססת הפרדיגמה הוא תחשיב למבדא, בניגוד למודל מכונת טיורינג עליו מתבססת הפרדיגמה האימפרטיבית, הנפוצה יותר.
מודל מכונת טיורינג הוא מודל שבו יש "מכונה" מופשטת המבצעת אלגוריתם - רצף פקודות שינויי מצב; בהתאם לכך, שפות אימפרטיביות מבצעות אלגוריתם - פעולה לאחר פעולה. לעומת זאת, תחשיב למבדא עוסק בחישוב של פונקציות - בעיקר חישוב רקורסיבי. הפרדיגמה הפונקציונלית רואה את הפעולה של תוכנית כפונקציה מקלט אל פלט - המחשב מקבל קלט מסוים, מבצע עליו חישוב ומוציא פלט. תוכנית פונקציונלית בנויה בהתאם, כרצף של הצהרות על פונקציות ומשתנים, שלאחריהם מתבצעת קריאה לפונקציה, הנעזרת בפונקציות אחרות, וכן הלאה, עד שבסופו של דבר מוחזר ערך כלשהו, והוא ערך הפלט - תוצאת החישוב (בהקשר זה פלט איננו הדפסה אל המסך, המדפסת, או כל רכיב חומרה אחר).
הוכח כי המודלים הללו שקולים, ולכן אין הבדל מבחינת כוח החישוב בין שפות התומכות בפרדיגמה הפונקציונלית לבין שפות אימפרטיביות טהורות (כגון שפת C). עם זאת, עשויים להיות הבדלים ניכרים בהיבט של נוחות ההבעה של חישובים מסוימים מול חישובים אחרים, קריאות, עקביות מתמטית, יעילות המימוש וכן הלאה.
ישנו דמיון מסוים בין תכנות פונקציונלי לתכנות לוגי, אך האחרון עוסק במבנים כלליים יותר - יחסים מול פונקציות שהן מקרה פרטי של יחסים.
פונקציות כערכים לכל דבר [עריכה]
פונקציות בשפות פונקציונלית הן עצמים בעלי קיום בלתי תלוי משל עצמם, כלומר הן ערכים לכל דבר, כמו מספרים או מחרוזות. ניתן לשלוח אותן לפונקציות אחרות כפרמטרים, והן יכולות לשמש כערכי החזרה.
תכנות פונקציונלי נקרא "טהור" אם אין בו שינוי מצב, כלומר אין בו משתנים או אובייקטים שמשנים את מצבם הפנימי. כל עיבוד מידע נעשה על ידי יצירת ערכים חדשים כנדרש. כתיבה כזו מקלה על הוכחות נכונות התוכנה ביחס למפרטיה. שינוי מצב של המערכת נקרא "תופעות לוואי". מכיוון שכל תקשורת של תוכנית עם העולם החיצון (קלט/פלט) משנה מצב של אובייקט ומסיבות נוספות, של נוחות או יעילות, גם שפות פונקציונליות "טהורות" כוללות תופעות לוואי, אך חלקן מרכזות את המבנים בעלי "תופעות הלוואי" במקום מוגדר, לדוגמה, במבנה שנקרא מונדה.
דוגמאות [עריכה]
חישוב עצרת [עריכה]
המקבילה ללולאה בשפות תכנות נפוצות היא הקריאה הרקורסיבית. לדוגמה, מימוש של חישוב 10 עצרת בשפת C עשוי להיראות כך:
int x = 1; for (int i = 1; i <= 10; i++) x *= i;
זהו מימוש אימפרטיבי: יש בתוכנית שני משתנים המשנים את ערכם תוך כדי ריצת התוכנית.
מימוש פונקציונלי של אותו חישוב ייראה כך:
fun fact 0 = 1 | fact n = n * (fact n-1) fact 10
המימוש הזה תואם ישירות את ההגדרה המתמטית הרקורסיבית של פעולת העצרת:
ותנאי ההתחלה
. במימוש זה, אין כל שינוי בערכי המשתנים! למעשה, המשתנים בשפות פונקציונליות אינם ברי שינוי: בכל קריאה לפונקציה נוצרים משתנים חדשים, המקבלים ערכים חדשים, וכן הלאה.
הסרת ערכים כפולים ומיון ברשימה [עריכה]
בדוגמה הבאה בשפת #C מתבצעת פעולה של הסרת ערכים כפולים מתוך משתנה רשימה המכיל מחרוזות ומיונם לאחר הסרת תווי רווח מקדימים. השיטה Distinct הקיימת באובייקט list1 מסירה את הערכים הכפולים, התוצאה מועברת לשיטה OrderBy הממיינת את הערכים, לאחר הסרת רווחים מקדימים, והתוצאה לבסוף מועברת לייצוג בצורת רשימה באמצעות השיטה ToList. ניתן לראות שבשורה אחת התבצעו מספר פקודות, ה-X בפונקציה של התחשיב למדא מייצג פונקציה אנונימית, שמחזירה את האיברים שב-list. מכיוון שבדוגמה ה-list מכיל רשימת מחרוזות, כל x מייצג מחרוזת, ולכן קיימת לו שיטה בשם TrimStart, שהיא שיטה הקיימת במחלקת מחרוזת.
private void DistinctAndOrderBy() { List<string> list1 = new List<string> {" BBB", "aaa", "hhh", " BBB", "aaa", " ZZZ", "hhh"," ZZZ" }; List<string> list2 = list1.Distinct().OrderBy(x => x.TrimStart()).ToList(); } // התוצאה שתתקבל היא: // list2 = aaa, BBB,hhh, ZZZ,
בפרדיגמה האימפרטיבית היה צריך ליצור לולאה שסורקת את כל האיברים ב-list1 ומסירה ערכים כפולים. בשלב השני היה צריך לסרוק את האיברים ולמיין אותם, או להפעיל פקודה הממיינת אותם. ולאחר מכן היה מתקבלת התוצאה ב-list2.