Common Lisp

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 המצוי בכל ניביה, או בעזרת <code>reader macro</code> שמנתח ומפרשן קלט של מחרוזת תווים לקוד ריצה, תכונה המאפשרת למשתמש ליצור תחביר מותאם אישית עם חוקיות חדשה לשפה בכל עת.

Common Lisp תומכת בתאימות לאחור עד רמה מסוימת עבור MacLisp ו־Lisp המקורית של ג'ון מקארתי, וזה מאפשר לשפה לתמוך בקוד Lisp מיושן מאמצע המאה ה־20.

== תחביר ==

=== ביטויים סימבולים (Symbolic Expressions) ===
ביטויים סימבולים (באנגלית: Symbolic Expressions או בקיצור, S-Expression) הם ביטויים אשר נמצאים בתוך סוגריים, מכילים בד״כ הוראה בראש הרשימה, ואז את האופרנדים (האיברים המשתתפים). הביטוי עובר הערכה לאחר השלמת הרשימה.

לדוגמה : <code>(3 2 5 +)</code> יחבר את 5, 3, ו־2, ויוערך ל־10.

ביטויים סימבולים יכולים להופיע באופן מקונן, כלומר, בתוך ביטויים סימבולים אחרים. לדוגמה,<syntaxhighlight lang="common-lisp">
(print (+ 5 2 3))
</syntaxhighlight>קוד זה יעריך את הביטוי האריתמטי <code>(3 2 5 +)</code> וידפיס את התוצאה באמצעות הפעולה המובנית <code>print</code>. הפעולה <code>print</code> היא פעולה אונארית (כלומר, מקבלת פרמטר יחיד) אשר תדפיס את הביטוי שניתן לה בשורה חדשה. על מנת לכתוב טקסט ללא מעבר לשורה חדשה, ניתן להשתמש בפעולה <code>write</code> באותו האופן.

=== פעולות חשבון ===
ארבע פעולות החשבון המרכזיות ב־Common Lisp, ובניבי Lisp בכלל, הם חיבור <code>+</code>, חיסור <code>-</code>, כפל <code>*</code>, וחילוק <code>/</code>. אמנם פעולות חשבון בסיסיות אלה קיימות בכלל שפות התכנות, במשפחת Lisp צורת הכתיבה שלהם היא תחילית (דומה לכתיב פולני במתמטיקה) עם מספר אופרנדים מרובה (מספר האיברים המשתתפים בפעולה) בניגוד לכתיב המסורתי במתמטיקה לפיו סימני פעולה מסומנים תמיד באופן בינארי בין שני אופרנדים בלבד המשתתפים בפעולה. הכתיב התחילי משותף גם לפונקציות כשהאיברים בביטוי הם הפרמטרים בהתאמה. בפעולות מובנות רבות אין מגבלה למספר האופרנדים. הפעולות חיבור, חיסור, כפל, וחילוק אינן פעולות בינאריות ב־Lisp. לדוגמה, החישוב <math>2 * 3 + 7 - 6</math> נכתב ב־Lisp כך,<syntaxhighlight lang="common-lisp">
( - (+ (* 2 3) 7) 6 )
</syntaxhighlight>

=== פעולות השוואה ===
פעולות השוואה ב־Lisp, כמו במתמטיקה בדידה, ובמדעי המחשב בכלל, משוות בין שני ביטויים או יותר ומחזירות ערך בוליאני (אמת או שקר). אמת אם הביטוי מתקיים, ושקר אם הביטוי אינו מתקיים. באופן מקביל, בשפות תכנות רבות משתמשים בפעולת השוואה בינארית (בדרך כלל מסומנת כ־<code>==</code>) על מנת לבדוק האם שני ביטויים שווים זה לזה. מפני שהתחביר בניבי Lisp משתמש בכתיב תחילי, אז פעולת ההשוואה (כמו עם שאר הפעולות) ממוקמת תמיד בתחילת הביטוי ויודעת להתמודד מול מספר אופרנדים מרובה (האיברים המשתתפים בהשוואה יכולים גם הם להיות ביטויי השוואה מקוננים). על מנת לבצע השוואה ב־Common Lisp ניתן להשתמש (בין היתר) בפעולה <code>=</code> בעלת arity אינסופי, וכן בפעולה הבינארית <code>eq</code>. בין הפעולות הללו ישנם הבדלים אחרים, לדוגמה,<syntaxhighlight lang="common-lisp">
(= (+ 5 5) 10 (+ 2 8) (+ 3 7 ))
-> T
(eq 4 2)
-> NIL
</syntaxhighlight>

=== פעולות <code>min</code> ו־<code>max</code> ===
הפעולות <code>max</code> ו־<code>min</code> קולטות מספר בלתי מוגבל של ערכים, ופולטות את הערך הכי גדול או הכי קטן בהתאמה. לדוגמה, הביטוי <code>(max 35 74 23 29 71)</code> יפלוט <code>74</code>. לעומת זאת <code>(min 35 74 23 29 71)</code> יפלוט <code>23</code>.

=== התניה ===
בקרת זרימה לפי תנאי נכתבת ב־Common Lisp בעזרת ההוראה <code>if</code>.

ניבי Lisp אחרים לעיתים תבוצע התניה באמצעות הוראת ה־<code>cond</code> (קיצור ל "condition").

מקומם של הפרמטרים בביטוי <code>if</code> מתארים את מבנה ההוראה,<syntaxhighlight lang="common-lisp">
(if (comparative-expression-either-true-or-false)
 (expression-to-be-evaluated-when-true)
 (expression-to-be-evaluated-when-false))
</syntaxhighlight>לדוגמה, קטע הקוד הבא ידפיס האם <math>6+4</math> שווה ל <math>3+7
</math>,<syntaxhighlight lang="common-lisp">
(if (eq (+ 3 7) (+ 6 4))
 (print "They are equal.")
 (print "They are not equal."))
</syntaxhighlight>

=== רשימות ===
[[רשימה מקושרת באופן חד־סטרי.]]
אחד ממבני הנתונים הנפוצים והשימושיים ביותר הוא רשימה מקושרת - אוסף של ערכים. ב־Common Lisp, ובניבי Lisp נוספים, ניתן ליצור רשימה מקושרת בשתי דרכים עיקריות, באמצעות <code>cons</code> ובאמצעות <code>list</code>. 

==== שימוש במילה השמורה <code>cons</code> ====
בשיטה זו, מגדירים את הרשימה המקושרת באופן רקורסיבי. <code>cons</code> קולט שני אופרנדים: הערך שבחוליה, והפניה לאיבר הבא. הרשימה נגמרת כאשר ההפניה לאיבר הבא היא <code>NIL</code> - כלום (השקול לערך הבוליאני <code>false</code> בשפות תכנות מסוימות). 

דוגמה להגדרה של רשימה מקושרת בעלת איבר אחד,<syntaxhighlight lang="common-lisp">
(cons 5 nil)
</syntaxhighlight>רשימה בעלת שני איברים,<syntaxhighlight lang="common-lisp">
(cons 1 2)
</syntaxhighlight>או,<syntaxhighlight lang="common-lisp">
(cons 1 (cons 2 nil))
</syntaxhighlight>שתי הרשימות תכילנה <code>(2 1)</code> .

דוגמה להגדרת רשימה בעלת 6 איברים,<syntaxhighlight lang="common-lisp">
(cons 16 (cons 22 (cons 43 (cons 82 (cons 41 (cons 93 nil))))))
</syntaxhighlight>הרשימה תכיל <code>(93 41 82 43 22 16)</code>.

==== שימוש במילה השמורה <code>list</code> ====
הדרך הקלה יותר ליצור רשימות מקושרות ב־Common Lisp ובניבי Lisp אחרים היא להשתמש בהוראת ה־<code>list</code>. 

לדוגמה,<syntaxhighlight lang="common-lisp">
(list 75 21 43 'hello 'world 98)
</syntaxhighlight>

=== יצירת משתנים ===
ניתן ליצור משתנים ב־Common Lisp ב־3 דרכים,

* באמצעות המאקרו <code>set</code> : דרך זו נחשבת למיושנת והייתה בשימוש נפוץ בגרסאות ישנות יותר של Lisp. דוגמה, <code>(set (quote *foo*) 42)</code>. בדוגמה זו נוצר משתנה בשם <code>*foo*</code> שערכו <code>42</code>. נעשה שימוש בהוראה <code>quote</code> על מנת ששם המשתנה, במקרה זה <code>*foo*</code>, יחשב ל־symbol ולא יוערך. ניתן לכתוב גרש (<code>'</code>) לפני הביטוי על מנת לקבל את אותה התוצאה (ניתן לבצע זאת ב־Common Lisp גם ללא קשר להגדרת משתנים). שם המשתנה מתחיל ונגמר בכוכבית כחלק ממוסכמת כתיבה בשפה, ואין חובה לבצע זאת.

* באמצעות המאקרו <code>setq</code> (קיצור ל־"set quote") לדוגמה: <code>(setq *foo* 42)</code>. גם בדוגמה זו, נוצר משתנה בשם <code>*foo*</code> שערכו <code>42</code>.
* באמצעות המאקרו <code>setf</code> : שיטה זו נחשבת לחדשנית יותר, משום שבנוסף ליכולותיהן של השיטות הקודמות. באמצעות <code>setf</code> ניתן לגשת ולשנות איברים ברשימה, לעומת <code>set</code> ו <code>setq</code>.

להלן השוואה בין הקוד הנדרש ליצירת רשימה זהה בשיטות השונות,<syntaxhighlight lang="common-lisp">
(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)
</syntaxhighlight>
=== פונקציות ===
ב־Common Lisp הגדרת פונקציות נעשית באמצעות המילה השמורה <code>defun</code> (קיצור ל־"define function") 

מבנה הגדרת פונקציה חדשה,<syntaxhighlight lang="common-lisp">
(defun name-of-function (parameters)
 "Optional documentation string."
 (body-expression))
</syntaxhighlight>דוגמה לפונקציית ריבוע (פרבולה) פשוטה. הפונקציה קולטת ערך ופולטת את הערך הריבועי שלו,<syntaxhighlight lang="common-lisp">
(defun f x
 (* x x))
</syntaxhighlight>דוגמה לפונקציה רקורסיבית אשר סוכמת את המספרים הטבעיים מ־0 עד למספר הטבעי הנתון. 

כלומר, עבור הפרמטר <math>n</math> נֵעשָה החישוב <math>{ 1+2+3\cdots+n }</math>. או בכתיב סכימה מסורתי, <math>\textstyle \sum_{k=0}^N \displaystyle</math>.<syntaxhighlight lang="common-lisp">
(defun summation(n)
 (if (eq n 0)
 0
 (+ (summation (- n 1)) n)))
</syntaxhighlight>על אותו עיקרון, יישום לאלגוריתם נאיבי לפעולת העצרת שקולט מספרים טבעיים בלבד,<syntaxhighlight lang="common-lisp">
(defun recursive-factorial(n)
 (if (eq n 0) 
 1
 (* (recursive-factorial (- n 1)) n)))
</syntaxhighlight>וכן פונקציה רקורסיבית אשר מצטמצמת (היות שגם היא ביטוי) למספר הגדול ביותר ברשימה הנקלטת אליה,<syntaxhighlight lang="common-lisp">
(defun recursive-maximum (item lst)
 (if (eq lst nil)
 item
 (recursive-maximum (max (car lst) item) (cdr lst))))
</syntaxhighlight>

==קישורים חיצוניים==

* 
* [https://www.cs.cmu.edu/~dst/LispBook/book.pdf Common LISP: A Gentle Introduction to Symbolic Computation]
* [https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node1.html Common Lisp the Language, 2nd Edition]

קטגוריה:שפות תכנות
קטגוריה:שפות תכנות מונחות עצמים
קטגוריה:שפות תכנות פונקציונליות
קטגוריה:ניבי Lisp