Common Lisp – הבדלי גרסאות

תוכן שנמחק תוכן שנוסף
Matanyabot (שיחה | תרומות)
מ בוט החלפות: מסוי\1, ,, לעיתים
הגהה, ניסוח, ויקיזציה, עיצוב, דקדוק עברית, תיקון פיסוק, הסרת שורות ריקות ורווחים מיותרים, שימוש במונחים בעברית ומונחים המקובלים במדעי המחשב. הסרת תוכן מבוא כללי ממדעי המחשב והוספת קישורים לערכים המורחבים באותם נושאים. סידור קטעי הקוד.
שורה 13:
}}
 
== היסטוריה ==
Common Lisp פותחה להיות ניב [[תקן|מתוקנן]] וטוב יותר של [[Maclisp|MacLisp]]. בתחילת שנות ה־80 כבר היו מספר קבוצות שעבדו על יורשים מגוונים ל־MacLisp כגון [[Lisp Machine Lisp]], [[Spice Lisp]], [[NIL]] ו־[[S-1 Lisp]]. מטרתה המרכזית של Common Lisp הייתה לאחד, לתקנן ולהרחיב את התכונות של כל אחד מהניבים האלה בשפה אחת. Common Lisp כשלעצמה אינה [[מימוש|יישום]] של [[Lisp]] אלא אפיון בלבד. קיימים מגוון יישומים ל־Common Lisp, ביניהם יישומים קנייניים, חינמיים וב[[קוד פתוח]]. Common Lisp היא שפה [[שפת תכנות רב־תכליתית|רב־תכליתית]] שמשלבת מספר [[פרדיגמת תכנות|פרדיגמות]] כגון תכנות [[תכנות פרוצדורלי|פרוצדורלי]], [[תכנות פונקציונלי|פונקציונלי]], ו[[תכנות מונחה-עצמים|מונחה־עצמים]]. מהיותה שפה [[תכנון דינמי|דינמית]], היא מאפשרת [[פיתוח איטרטיבי והדרגתי]] נוח יותר שנעשה תוך כדי [[זמן ריצה (תוכנה)|זמן הריצה]] של ה[[תוכנה]] מבלי להפריע לה.
Common Lisp פותחה להיות ניב [[תקן|מתוקנן]] וטוב יותר של [[Maclisp|MacLisp]]. בתחילת שנות ה־80 כבר היו מספר קבוצות שעבדו על יורשים מגוונים ל־MacLisp כגון [[Lisp Machine Lisp]], [[Spice Lisp]], [[NIL]] ו־[[S-1 Lisp]]. מטרתה המרכזית של Common Lisp הייתה לאחד, לתקנן ולהרחיב את התכונות של כל אחד מהניבים האלה בשפה אחת. Common Lisp כשלעצמה אינה [[מימוש|יישום]] של [[Lisp]] אלא אפיון בלבד. קיימים מגוון יישומים ל־Common Lisp, ביניהם יישומים קנייניים, חינמיים וב[[קוד פתוח]].
 
== מאפיינים ==
Common Lisp היא שפה [[שפת תכנות רב־תכליתית|רב־תכליתית]] שמשלבת מספר [[פרדיגמת תכנות|פרדיגמות]] כגון תכנות [[תכנות פרוצדורלי|פרוצדורלי]], [[תכנות פונקציונלי|פונקציונלי]], ו[[תכנות מונחה-עצמים|מונחה־עצמים]]. מהיותה שפה [[תכנון דינמי|דינמית]], היא מאפשרת [[פיתוח איטרטיבי והדרגתי]] נוח יותר שנעשה תוך כדי [[זמן ריצה (תוכנה)|זמן הריצה]] של ה[[תוכנה]] מבלי להפריע לה.
 
Common Lisp גם מאפשרת ביאור טיפוסים והמרתם עבור שלבי אופטימיזציה מתקדמים בזמן הפיתוח כדי לאפשר למהדר לחולל קוד יעיל יותר בביצועים. בנוסף ניתן להצהיר בעבור כל פונקציה או מרכיב בקוד בנפרד באיזה רמה של [[Type safety|תאימות טיפוסית]] רוצים להשתמש כדי למנוע אי־התאמה בין טיפוסים שונים.
שורה 23 ⟵ 27:
Common Lisp תומכת ב[[תאימות לאחור]] עד רמה מסוימת עבור [[Maclisp]] ו־[[Lisp]] המקורית של [[ג'ון מקארתי]], וזה מאפשר לשפה לתמוך בקוד מיושן מאמצע המאה ה־20.
 
== תחביר ==
== כתיבת קוד ב-Common Lisp ==
 
=== ביטויים סימבולים ( Symbolic Expressions ) ===
'''ביטויים סימבולים''' ( באנ׳באנגלית: '''Symbolic Expressions''' או בקיצור, '''S Expressions-Expression''' ) הם ביטויים אשר נמצאים אשר נמצאים בתוך סוגריים, מכילים בד״כ הוראה ואופרנדיםבראש הרשימה, ומפושטיםואז את האופרנדים (האיברים המשתתפים). הביטוי עובר הערכה לאחר מכןהשלמת הרשימה.
 
לדוגמה : <code>(3 2 5 +)</code> יחבר את 5, 3, ו-2ו־2, ויפושטויוערך ל-10ל־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 צורת הכתיבה שלהם היא תחילית (דומה ל[[כתיב פולני]] במתמטיקה) עם מספר אופרנדים {{אנג|arity}} מרובה (מספר האיברים המשתתפים בפעולה) בניגוד לכתיב המסורתי במתמטיקה לפיו סימני פעולה מסומנים תמיד באופן בינארי בין שני אופרנדים בלבד המשתתפים בפעולה. הכתיב התחילי משותף גם לפונקציות כשהאיברים בביטוי הם הפרמטרים בהתאמה. בפעולות מובנות רבות אין מגבלה למספר האופרנדים. הפעולות חיבור, חיסור, כפל, וחילוק אינן פעולות בינאריות ב־Lisp. לדוגמה, החישוב <math>2 * 3 + 7 - 6</math> נכתב ב־Lisp כך,<syntaxhighlight lang="common-lisp">
ארבעת האופרטורים ה[[אריתמטיקה|אריתמטיים]] המשמעותיים ביותר ב-Common Lisp, ובניבי Lisp בכלל, הם חיבור <code>+</code> , חיסור <code>-</code> , כפל <code>*</code>, וחילוק <code>/</code> .
 
אמנם אופרטורים אלה הם חלק אינטגרלי ממתמטיקה ואריתמטיקה בסיסית, וקיימים בכלל שפות התכנות, במשפחת Lisp צורת הכתיבה שלהם שונה. ב-Lisp ישנו שימוש בכתיב תחילי, כלומר, האופרטורים והפעולות בה מופיעים בהתחלה, ושאר האיברים בביטוי הם האופרנדים, או הפרמטרים בהתאמה. באופרטורים ופעולות מובנות רבות אין מגבלה לכמות האופרנדים שניתן להכניס, כלומר, חיבור,חיסור, כפל וחילוק למשל, אינן פעולות בינאריות ב-Lisp. לדוגמה, על מנת לבצע את הפעולה האריתמטית <math>2 * 3 + 7 - 6</math>, נכתוב את הקוד הבא:<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">
אופרטורים השוואתיים במדעי המחשב משווים בין שני ביטויים או יותר. דוגמאות לאופרטורים השוואתיים קיימות גם במתמטיקה, ( <math>= </math>, <math>\neq </math>, <math>></math>, <math><</math>, <math>\geq</math>, <math>\leq</math> וכו׳ ).
 
הם מחזירים ערך בוליאני (כלומר, אמת / שקר ): '''אמת''' במידה והתנאי ההשוואתי מתקיים, ו'''שקר''' במידה והתנאי אינו מתקיים. למשל, בשפות תכנות עיליות, כגון C#, Java, Python ועוד משתמשים באופרטור <code>==</code> על מנת לבדוק האם שני ביטויים שווים זה לזה. לדוגמה, <code>5==4</code> יחזיר '''שקר''', אך 7==7 יחזיר '''אמת''' . מפני שהתחביר בניבי Lisp, משתמש בכתיב תחילי (בדומה ל[[כתיב פולני]]), אופרטורי ההשוואה לרוב ממוקמים באופן שונה, ומקבלים מספר אופרנדים שונה. על מנת לבצע השוואה ב-Common Lisp ניתן להשתמש (בין היתר) באופרטור <code>=</code> שיכול לקבל מספר בלתי מוגבל של אופרנדים, וכן באופרטור <code>eq</code> אשר מקבל 2 איברים בלבד. בין האופרטורים הללו ישנם הבדלים אחרים. לדוגמה:<syntaxhighlight lang="common-lisp">
(= (+ 5 5) 10 (+ 2 8) (+ 3 7 ))
-> T
שורה 51:
</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>.
 
=== התניה ===
למשל, <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">
ב-Common Lisp, על מנת לבצע התניות יש להשתמש בהוראת ה-if. בניבי Lisp אחרים לעיתים תבוצע התניה באמצעות הוראת ה-cond ( קיצור של condition ).
(if (comparative-expression-either-true-or-false)
 
(expression-to-be-evaluated-when-true)
מבנה הבלוק של '''if''' : <syntaxhighlight>
(expression-to-be-evaluated-when-false))
( if ( שקר /אמת )
</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>לדוגמה, ניצור קטע קוד אשר ידפיס האם <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>
 
=== רשימות ===
[[קובץ:Singly-linked-list.svg|ממוזער|304x304 פיקסלים|[[רשימה מקושרת]] (linked list) באופן חד־סטרי.]]
אחד ממבני הנתונים הנפוצים והשימושיים ביותר הוא [[רשימה מקושרת]] - אוסף של ערכים. ב־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">
אחד ממבני הנתונים הנפוצים והשימושיים ביותר הוא [[רשימה מקושרת]] - אוסף של ערכים. ב-Common Lisp, ובניבי Lisp נוספים, ניתן ליצור רשימה מקושרת ב-2 דרכים עיקריות:
[[קובץ:Singly-linked-list.svg|ממוזער|304x304 פיקסלים|Singly-linked-list]]
 
# '''שימוש ב[[מילה שמורה|מילה השמורה]]''' <code>cons</code>''':''' בשיטה זו, מגדירים את הרשימה המקושרת באופן רקורסיבי. cons מקבל שני אופרנדים : הערך שבחוליה, והפניה לאיבר הבא. הרשימה נגמרת כאשר ההפניה לאיבר הבא היא <code>NIL</code> - כלום ( ההקבלה לערך הבוליאני <code>false</code> בשפות תכנות אחרות) דוגמה להגדרה של רשימה מקושרת בעלת איבר אחד ב-Common Lisp :
<syntaxhighlight lang="common-lisp">
(cons 5 nil)
</syntaxhighlight>רשימה בעלת 2שני איברים:,<syntaxhighlight lang="common-lisp">
(cons 1 2)
</syntaxhighlight>או,<syntaxhighlight lang="common-lisp">
(cons 1 (cons 2 nil))
</syntaxhighlight>שתי הרשימות יכילו <code>(2 1)</code> .
שורה 92 ⟵ 87:
דוגמה להגדרת רשימה בעלת 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>.
 
2.'''==== שימוש במילה השמורה''' <code>list</code>''':''' ====
הדרך הקלה יותר ליצור רשימות מקושרות ב-Commonב־Common Lisp ובניבי Lisp אחרים היא להשתמש בהוראת ה-ה־<code>list</code>.

לדוגמה:,<syntaxhighlight lang="common-lisp">
(list 75 21 43 'hello 'world 98)
</syntaxhighlight>
 
=== יצירת משתנים ===
ניתן ליצור משתנים ב-Commonב־Common Lisp ב-3ב־3 דרכים:
 
# דרך זו היא מיושנת יותר והייתה בשימוש רב יותר בגרסאות ישנות יותר של Lisp. ניצור משתנה* '''באמצעות ה[[מאקרו (תכנות)|מאקרו]] <code>set</code>''' דרך זו נחשבת למיושנת והייתה בשימוש נפוץ בגרסאות ישנות יותר של Lisp. לדוגמהדוגמה: <code>(set (quote *foo*) 42)</code> . בדוגמה זו יצרנונוצר משתנה בשם <code>*foo*</code> שערכו <code>42</code>. השתמשנונעשה שימוש בהוראה <code>quote</code> על מנת ששם המשתנה, במקרה זה <code>*foo*</code>, יחשב ל-symbolל־symbol ולא יפושטיוערך. ניתן לכתוב גרש ( ׳ ') לפני הביטוי על מנת לקבל את אותה התוצאה ( ניתן לבצע זאת ב-Commonב־Common Lisp גם ללא קשר להגדרת משתנים ). שם המשתנה מתחיל ונגמר בכוכבית ככחלק ממוסכמת כתיבה בשפה, ואין חובה לבצע זאת גם.
# באמצעות המאקרו <code>setq</code> ( קיצור של set quote ), לדוגמה: <code>(setq *foo* 42)</code>. גם בדוגמה זו, יצרנו משתנה בשם *foo* שערכו 42.
# באמצעות המאקרו setf . שיטה זו נחשבת לחדשנית יותר, משום שבנוסף ליכולותיהן של השיטות הקודמות. באמצעות setf ניתן לגשת ולשנות איברים ברשימה, לעומת set ו setq.
 
* '''באמצעות המאקרו''' <code>'''setq'''</code> (קיצור ל־"set quote") לדוגמה: <code>(setq *foo* 42)</code>. גם בדוגמה זו, נוצר משתנה בשם <code>*foo*</code> שערכו <code>42</code>.
להלן השוואה בין הקוד הנדרש יצירת רשימה זהה בשיטות השונות:<syntaxhighlight lang="common-lisp">
* '''באמצעות המאקרו <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)
שורה 118 ⟵ 117:
(1 2 3)
</syntaxhighlight>
=== פונקציות ===
ב־Common Lisp הגדרת פונקציות נעשית באמצעות ה[[מילה שמורה|מילה השמורה]] <code>defun</code> (קיצור ל־"define function")
 
מבנה הגדרת פונקציה חדשה,<syntaxhighlight lang="common-lisp">
=== פעולות ===
בתכנות פונקציונלי ופרוצדורלי, נהוג להגדיר פעולות שיכולות לקבל פרמטרים שונים. כך נמנעת חזרה של אותו קוד מספר פעמים, ונשמר הסדר והיעילות בקוד. ב-Common Lisp הגדרת פעולות נעשית באמצעות ה[[מילה שמורה|מילה השמורה]] <code>defun</code>.
 
מבנה בלוק של פעולה:<syntaxhighlight lang="common-lisp">
(defun name-of-function (parameters)
"Optional documentation string."
(body-expression))
</syntaxhighlight>דוגמה לפונקצית ריבוע ([[פרבולה]]) פשוטה. הפונקציה קולטת ערך ופולטת את הערך הריבועי שלו,<syntaxhighlight lang="common-lisp">
)
(defun f x
</syntaxhighlight>דוגמה לפעולה אשר מפושטת לפרמטר ( בדוגמה זו: <code>number1</code>) בריבוע:<syntaxhighlight lang="common-lisp">
(* x x))
(defun power number1
</syntaxhighlight>דוגמה לפונקציה [[רקורסיה|רקורסיבית]] אשר [[סכום|סוכמת]] את [[מספר טבעי|המספרים הטבעיים]] מ־0 עד למספר הטבעי הנתון.
(* number1 number1)
 
)
</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 sum_tosummation( num1 n)
( if ( eq num1n 0 )
0
(+ (sum_tosummation ( - num1n 1)) n)) num1 )
</syntaxhighlight>על אותו עיקרון, היישום הנאיבי (ללא הטמנה) לפעולת ה[[עצרת]] הקולטת מספרים טבעיים בלבד יכתב כך,<syntaxhighlight lang="common-lisp">
)
(defun recursive-factorial(n)
)
(if (eq n 0)
</syntaxhighlight>ועל אותו עיקרון, מימוש רקורסיבי ב-Common Lisp לפעולת ה[[עצרת]] ב[[מתמטיקה]], שמקבלת [[מספרים חיוביים ושליליים|מספרים אי שליליים]] ושלמים בלבד:<syntaxhighlight lang="common-lisp">
1
(defun factorial_of( num1 )
( if (* eq(recursive-factorial num1(- 0n 1)) n)))
</syntaxhighlight>וכן פונקציה רקורסיבית אשר פולטת את המספר הגדול ביותר ב[[רשימה (מבנה נתונים)|רשימה]] הנקלטת אליה,<syntaxhighlight lang="common-lisp">
1
(defun recursive-maximum (item lst)
(* (factorial_of ( - num1 1 )) num1 )
(if (eq lst nil)
) item
(recursive-maximum (max (car lst) item) (cdr lst))))
)
</syntaxhighlight>וכן פעולה רקורסיבית אשר מפושטת למספר הגדול ביותר ב[[רשימה (מבנה נתונים)|רשימה]] המוכנסת אליה:<syntaxhighlight lang="common-lisp">
(defun max_item ( item lst )
( if ( eq lst nil )
item
(max_item (max (car lst ) item ) (cdr lst) )
)
)
</syntaxhighlight>