Common Lisp
פרדיגמות | פונקציונלי, פרוצדורלי, השתקפותי, מטא, גנרי |
---|---|
תאריך השקה | 1984, 1994 עבור ANSI Common Lisp |
מפתח | ANSI X3J13 committee |
טיפוסיות | חזקה, דינמית |
מימושים | Allegro CL, ABCL, CLISP, Clozure CL, CMUCL, ECL, GCL, LispWorks, Scieneer CL, SBCL, Symbolics Common Lisp |
הושפעה על ידי | Lisp, Lisp Machine Lisp, Maclisp, Scheme, Interlisp |
השפיעה על | Clojure, Dylan, Emacs Lisp, EuLisp, ISLISP, Julia, Moose, R, SKILL, SubL |
סיומת | .lisp, .lsp, .l, .cl, .fasl |
Common Lisp (בראשי תיבות: CL) היא ניב של שפת התכנות Lisp, שפורסמה לראשונה ב־1984 ועברה תקינה על ידי ANSI בשנת 1994 ופורסמה שוב כ־ANSI Common Lisp.
היסטוריה
Common Lisp פותחה להיות ניב מתוקנן וטוב יותר של MacLisp. בתחילת שנות ה־80 כבר היו מספר קבוצות שעבדו על יורשים מגוונים ל־MacLisp כגון Lisp Machine Lisp, Spice Lisp, NIL ו־S-1 Lisp. מטרתה המרכזית של Common Lisp הייתה לאחד, לתקנן ולהרחיב את התכונות של כל אחד מהניבים האלה בשפה אחת. Common Lisp כשלעצמה אינה יישום של Lisp אלא אפיון בלבד. קיימים מגוון יישומים ל־Common Lisp, ביניהם יישומים קנייניים, חינמיים ובקוד פתוח.
מאפיינים
Common Lisp היא שפה רב־תכליתית שמשלבת מספר פרדיגמות כגון תכנות פרוצדורלי, פונקציונלי, ומונחה־עצמים. מהיותה שפה דינמית, היא מאפשרת פיתוח איטרטיבי והדרגתי נוח יותר שנעשה תוך כדי זמן הריצה של התוכנה מבלי להפריע לה.
השפה מאפשרת ביאור טיפוסים והמרתם עבור שלבי אופטימיזציה מתקדמים בזמן הפיתוח כדי לאפשר למהדר לחולל קוד יעיל יותר בביצועים. בנוסף ניתן להצהיר בעבור כל פונקציה או מרכיב בקוד בנפרד באיזה רמה של תאימות טיפוסית רוצים להשתמש כדי למנוע אי־התאמה בין טיפוסים שונים.
אפיון השפה כולל את CLOS (שם מלא: Common Lisp Object System) מערכת מובנת לתמיכה בתכנות מונחה־עצמים שתומכת במולטי־מתודות, למעשה הן פונקציות שזיהוי התאימות בינן לבין טיפוסיות הפרמטרים נעשה באופן דינמי בזמן ריצה. תכונה שמאפשרת למתודות לא להשתייך לאף טיפוס מלכתחילה ולהישאר סטטיות בהתאם לפרדיגמת הפונקציונליות של השפה Lisp.
Common Lisp היא שפת שניתנת להרחבה בעזרת קוד מאקרו סטנדרטי של Lisp המצוי בכל ניביה, או בעזרת reader macro
שמנתח ומפרשן קלט של מחרוזת תווים לקוד ריצה, תכונה המאפשרת למשתמש ליצור תחביר מותאם אישית עם חוקיות חדשה לשפה בכל עת.
Common Lisp תומכת בתאימות לאחור עד רמה מסוימת עבור MacLisp ו־Lisp המקורית של ג'ון מקארתי, וזה מאפשר לשפה לתמוך בקוד Lisp מיושן מאמצע המאה ה־20.
תחביר
ביטויים סימבולים (Symbolic Expressions)
ביטויים סימבולים (באנגלית: Symbolic Expressions או בקיצור, S-Expression) הם ביטויים אשר נמצאים בתוך סוגריים, מכילים בד״כ הוראה בראש הרשימה, ואז את האופרנדים (האיברים המשתתפים). הביטוי עובר הערכה לאחר השלמת הרשימה.
לדוגמה : (3 2 5 +)
יחבר את 5, 3, ו־2, ויוערך ל־10.
ביטויים סימבולים יכולים להופיע באופן מקונן, כלומר, בתוך ביטויים סימבולים אחרים. לדוגמה,
(print (+ 5 2 3))
קוד זה יעריך את הביטוי האריתמטי (3 2 5 +)
וידפיס את התוצאה באמצעות הפעולה המובנית print
. הפעולה print
היא פעולה אונארית (כלומר, מקבלת פרמטר יחיד) אשר תדפיס את הביטוי שניתן לה בשורה חדשה. על מנת לכתוב טקסט ללא מעבר לשורה חדשה, ניתן להשתמש בפעולה write
באותו האופן.
פעולות חשבון
ארבע פעולות החשבון המרכזיות ב־Common Lisp, ובניבי Lisp בכלל, הם חיבור +
, חיסור -
, כפל *
, וחילוק /
. אמנם פעולות חשבון בסיסיות אלה קיימות בכלל שפות התכנות, במשפחת Lisp צורת הכתיבה שלהם היא תחילית (דומה לכתיב פולני במתמטיקה) עם מספר אופרנדים (arity) מרובה (מספר האיברים המשתתפים בפעולה) בניגוד לכתיב המסורתי במתמטיקה לפיו סימני פעולה מסומנים תמיד באופן בינארי בין שני אופרנדים בלבד המשתתפים בפעולה. הכתיב התחילי משותף גם לפונקציות כשהאיברים בביטוי הם הפרמטרים בהתאמה. בפעולות מובנות רבות אין מגבלה למספר האופרנדים. הפעולות חיבור, חיסור, כפל, וחילוק אינן פעולות בינאריות ב־Lisp. לדוגמה, החישוב נכתב ב־Lisp כך,
( - (+ (* 2 3) 7) 6 )
פעולות השוואה
פעולות השוואה ב־Lisp, כמו במתמטיקה בדידה, ובמדעי המחשב בכלל, משוות בין שני ביטויים או יותר ומחזירות ערך בוליאני (אמת או שקר). אמת אם הביטוי מתקיים, ושקר אם הביטוי אינו מתקיים. באופן מקביל, בשפות תכנות רבות משתמשים בפעולת השוואה בינארית (בדרך כלל מסומנת כ־==
) על מנת לבדוק האם שני ביטויים שווים זה לזה. מפני שהתחביר בניבי Lisp משתמש בכתיב תחילי, אז פעולת ההשוואה (כמו עם שאר הפעולות) ממוקמת תמיד בתחילת הביטוי ויודעת להתמודד מול מספר אופרנדים מרובה (האיברים המשתתפים בהשוואה יכולים גם הם להיות ביטויי השוואה מקוננים). על מנת לבצע השוואה ב־Common Lisp ניתן להשתמש (בין היתר) בפעולה =
בעלת arity אינסופי, וכן בפעולה הבינארית eq
. בין הפעולות הללו ישנם הבדלים אחרים, לדוגמה,
(= (+ 5 5) 10 (+ 2 8) (+ 3 7 ))
-> T
(eq 4 2)
-> NIL
פעולות min
ו־max
הפעולות max
ו־min
קולטות מספר בלתי מוגבל של ערכים, ופולטות את הערך הכי גדול או הכי קטן בהתאמה. לדוגמה, הביטוי (max 35 74 23 29 71)
יפלוט 74
. לעומת זאת (min 35 74 23 29 71)
יפלוט 23
.
התניה
בקרת זרימה לפי תנאי נכתבת ב־Common Lisp בעזרת ההוראה if
.
ניבי Lisp אחרים לעיתים תבוצע התניה באמצעות הוראת ה־cond
(קיצור ל "condition").
מקומם של הפרמטרים בביטוי if
מתארים את מבנה ההוראה,
(if (comparative-expression-either-true-or-false)
(expression-to-be-evaluated-when-true)
(expression-to-be-evaluated-when-false))
לדוגמה, קטע הקוד הבא ידפיס האם שווה ל ,
(if (eq (+ 3 7) (+ 6 4))
(print "They are equal.")
(print "They are not equal."))
רשימות
אחד ממבני הנתונים הנפוצים והשימושיים ביותר הוא רשימה מקושרת - אוסף של ערכים. ב־Common Lisp, ובניבי Lisp נוספים, ניתן ליצור רשימה מקושרת בשתי דרכים עיקריות, באמצעות cons
ובאמצעות list
.
שימוש במילה השמורה cons
בשיטה זו, מגדירים את הרשימה המקושרת באופן רקורסיבי. cons
קולט שני אופרנדים: הערך שבחוליה, והפניה לאיבר הבא. הרשימה נגמרת כאשר ההפניה לאיבר הבא היא NIL
- כלום (השקול לערך הבוליאני false
בשפות תכנות מסוימות).
דוגמה להגדרה של רשימה מקושרת בעלת איבר אחד,
(cons 5 nil)
רשימה בעלת שני איברים,
(cons 1 2)
או,
(cons 1 (cons 2 nil))
שתי הרשימות תכילנה (2 1)
.
דוגמה להגדרת רשימה בעלת 6 איברים,
(cons 16 (cons 22 (cons 43 (cons 82 (cons 41 (cons 93 nil))))))
הרשימה תכיל (93 41 82 43 22 16)
.
שימוש במילה השמורה list
הדרך הקלה יותר ליצור רשימות מקושרות ב־Common Lisp ובניבי Lisp אחרים היא להשתמש בהוראת ה־list
.
לדוגמה,
(list 75 21 43 'hello 'world 98)
יצירת משתנים
ניתן ליצור משתנים ב־Common Lisp ב־3 דרכים,
- באמצעות המאקרו
set
: דרך זו נחשבת למיושנת והייתה בשימוש נפוץ בגרסאות ישנות יותר של Lisp. דוגמה,(set (quote *foo*) 42)
. בדוגמה זו נוצר משתנה בשם*foo*
שערכו42
. נעשה שימוש בהוראהquote
על מנת ששם המשתנה, במקרה זה*foo*
, יחשב ל־symbol ולא יוערך. ניתן לכתוב גרש ('
) לפני הביטוי על מנת לקבל את אותה התוצאה (ניתן לבצע זאת ב־Common Lisp גם ללא קשר להגדרת משתנים). שם המשתנה מתחיל ונגמר בכוכבית כחלק ממוסכמת כתיבה בשפה, ואין חובה לבצע זאת.
- באמצעות המאקרו
setq
(קיצור ל־"set quote") לדוגמה:(setq *foo* 42)
. גם בדוגמה זו, נוצר משתנה בשם*foo*
שערכו42
. - באמצעות המאקרו
setf
: שיטה זו נחשבת לחדשנית יותר, משום שבנוסף ליכולותיהן של השיטות הקודמות. באמצעותsetf
ניתן לגשת ולשנות איברים ברשימה, לעומתset
וsetq
.
להלן השוואה בין הקוד הנדרש ליצירת רשימה זהה בשיטות השונות,
(set (quote foo) (list 1 2 3)) ;foo => (1 2 3)
(1 2 3)
(set 'foo '(1 2 3)) ;foo => (1 2 3) same function, simpler expression
(1 2 3)
(setq foo '(1 2 3)) ;foo => (1 2 3) similar function, different syntax
(1 2 3)
(setf foo '(1 2 3)) ;foo => (1 2 3) more capable function
(1 2 3)
פונקציות
ב־Common Lisp הגדרת פונקציות נעשית באמצעות המילה השמורה defun
(קיצור ל־"define function")
מבנה הגדרת פונקציה חדשה,
(defun name-of-function (parameters)
"Optional documentation string."
(body-expression))
דוגמה לפונקציית ריבוע (פרבולה) פשוטה. הפונקציה קולטת ערך ופולטת את הערך הריבועי שלו,
(defun f x
(* x x))
דוגמה לפונקציה רקורסיבית אשר סוכמת את המספרים הטבעיים מ־0 עד למספר הטבעי הנתון. כלומר, עבור הפרמטר נֵעשָה החישוב . או בכתיב סכימה מסורתי, .
(defun summation(n)
(if (eq n 0)
0
(+ (summation (- n 1)) n)))
על אותו עיקרון, יישום לאלגוריתם נאיבי לפעולת העצרת שקולט מספרים טבעיים בלבד,
(defun recursive-factorial(n)
(if (eq n 0)
1
(* (recursive-factorial (- n 1)) n)))
וכן פונקציה רקורסיבית אשר מצטמצמת (היות שגם היא ביטוי) למספר הגדול ביותר ברשימה הנקלטת אליה,
(defun recursive-maximum (item lst)
(if (eq lst nil)
item
(recursive-maximum (max (car lst) item) (cdr lst))))
קישורים חיצוניים
- אתר האינטרנט הרשמי של Common Lisp
- Common LISP: A Gentle Introduction to Symbolic Computation
- Common Lisp the Language, 2nd Edition
32645893Common Lisp