24.9.2004 גירסה 1.00 שפות תכנות מסמך זה הורד מהאתר. אין להפיץ מסמך זה במדיה כלשהי, ללא אישור מפורש מאת המחבר. מחבר המסמך איננו אחראי לכל נזק, ישיר או עקיף, שיגרם עקב השימוש במידע המופיע במסמך, וכן לנכונות התוכן של הנושאים המופיעים במסמך. עם זאת, המחבר עשה את מירב המאמצים כדי לספק את המידע המדויק והמלא ביותר. Nir Adar Email: underwar@hotmail.com Home Page: כל הזכויות שמורות ל אנא שלחו תיקונים והערות אל המחבר. -1-
1. תוכן עניינים 2 1. תוכן עניינים 5 2. מבוא 5 6 6 סוגי שפות דרישות משפת תכנות סיווגשפות תכנות.2.1.2.2.2.3 7 3. ערכים וסוגים 7 7 7 8 8 9 9 9 10 10 11 13 14 14 15 16 16 16 17 18 18 ערכים מבנה הערכים מניפולציות על ערכים ערכים בשפת PASCAL ערכים בשפת ML סוגי ערכים סוגי ערכים כקבוצות של ערכים סוגים בעלי סדר SEMI-PRIMITIVE TYPES מחרוזות COMPOSITE TYPES סוגים רקורסיביים שימוש בערכים ערכי COMPOSITE כערכים ממחלקה ראשונה פונקציות כערכים ממחלקה ראשונה ביטויים (EXPRESSIONS) קבוע (LITERAL) קבועים ומשתנים AGGREGATES ביטויי תנאי קריאות לפונקציות.3.1.3.1.1.3.1.2.3.1.3.3.1.4.3.2.3.2.1.3.2.2.3.2.3.3.2.4 3.2.5..3.2.6.3.3.3.3.1.3.3.2.3.4.3.3.3.3.3.4 3.3.5..3.3.6.3.3.7-2-
19 19 20 20 21 21 23 23 24 24 25 26 28 מערכות סוגים בשפות תכנות סיווג מערכת סוגים קיום שקילות בין סוגים EQUIVALENCE) (TYPE זהות מבנה EQUIVALENCE) (STRUCTURAL זהות בשם הסוג - EQUIVALENCE NAME זהות הצהרתית תת סוגים ב- PASCAL סוגים נגזרים ו- SUBTYPING בשפת ADA חוזק מערכת הסוגים זמן הבדיקה תת טיפוס TYPE SUB ARITHMETIC TYPES.3.5.3.5.1.3.5.2.3.5.3.3.5.4.3.5.5.3.5.6 3.5.7..3.5.8.3.5.9.3.5.10.3.5.11.3.5.12 30 POLYMORPHISM.4 30 31 31 32 33 33 34 36 37 38 38 מונומורפיזם סוגים של פולימורפיזם UNIVERSAL POLYMORPHISM מול AD HOC POLYMORPHISM מערכות מונומורפיות מול מערכות פולימורפיות OVERLOADING COERCIONS PARAMETRIC POLYMORPHISM POLYTYPES הערכים של POLYTYPE משמעות ל- POLYTYPE INCLUSION POLYMORPHISM.4.1.4.2.4.2.1.4.3.4.3.1.4.3.2.4.3.3.4.3.4.4.3.5.4.3.6 4.3.7. 39 STORAGE 5. 39 40 40 41 41 42 COMPOSITE VARIABLES המבנה של COMPOSITE VARIABLES ARRAY VARIABLES STORABLES LIFETIME משתנים לוקליים וגלובליים.5.1.5.1.1.5.1.2.5.1.3.5.1.4.5.1.5 43.5.2 PERSISTENT VARIABLES -3-
44 DANGLING REFERENCES 5.3. 45 6. פקודות 45 45 45 45 45 46 46 47 47 47 47 47 48 48 49 50 אבני הבניין של השפה פקודות SKIPS סיום פקודה הערות השמות קריאות לפונקציות בקרת זרימה SEQUENTIAL AND COLLATERAL COMMANDS CONDITIONAL COMMANDS לולאות COLLATERAL LOOPS משתני לולאה POWER LOOPS ביטויים עם SIDE-EFFECTS SEQUENCERS.6.1.6.1.1.6.1.2.6.1.3.6.1.4.6.1.5.6.1.6.6.2.6.2.1.6.2.2.6.2.3.6.2.4.6.2.5.6.2.6 6.2.7..6.2.8 52 BINDINGS 7. 52 53 54 55 BLOCK STRUCTURE STATIC AND DYNAMIC BINDING הצהרות בלוקים 7.1..7.2.7.3.7.4-4-
2. מבוא המחשב מבין שפת מכונה בלבד. לעומת זאת, לבני אדם קשה לכתוב בשפת מכונה במהירות ובלי שגיאות. שפת תכנות זוהי הפשטה עבור מתכנתים, שהיא "פשרה" בין השפה המובנת למחשב לשפה המובנת לבני אדם. נתעניין במסמך זה בהבנת ההבדלים בין שפות מה מייחד שפה אחת על פני שפה אחרת. בדומה למספר מסמכים נוספים שלי מהתקופה האחרונה, מסמך זה דורש עבודה נוספת כדי להוות מדריך שלם לנושא זה. אם זאת, אין לי שום עניין להפוך אותו לכזה. המסמך נכתב לצורך שימוש אישי, וכעת אני מפרסם אותו כפי שהוא, מתוך כוונה לנסות לעזור לאנשים אחרים הבאים ללמוד את הנושא, וללא התיימרות ליצור מסמך שלם לגמרי המכיל את כל הפרטים. כולי תקווה שמסמך זה יהיה לכם לעזר. 2.1. סוגי שפות שפת אסמבלר כל פקודה בשפת אסמבלר מתרגמת ישירות ובאופן מוגדר וידוע לפקודה בשפת מכונה. יתרון: המעבר משפת אסמבלר לשפת מכונה הוא ברור ופשוט. חסרונות: קשה להבין את הקוד (את המשמעות שלו), סיכוי רב לטעויות. שפות אימפרטיביות שפות אימפרטיביות הינן שפות כגון C ו- Pascal, המתבססות על שינוי מצב (של זיכרון, של משתנים) כגורם המכוון של התוכנית. יתרונות: קל יותר לכתוב בשפה אימפרטיבית מלכתוב בשפת אסמבלר, המימוש של הפקודות בשפת מכונה פשוט בד"כ. חסרונות: יעיל פחות מכתיבה ישירה באסמבלר. שפות פונקציונליות שפות פונקציונליות כוללות את שפת ML, LISP ועוד. שפה פונקציונלית שפה היא ללא מצבים. אנו משתמשים בביטויים רקורסיביים כדי לחשב את התוצאה. חסרון: המימוש של שפה פונקציונלית בשפת מכונה אינו טריויאלי. יתרון: משמעות הקוד ברורה. הקוד קריא יותר לבני אדם. -5-
2.2. דרישות משפת תכנות בעלת כושר ביטוי: נדרוש שהשפה תאפשר לנו לבטא מגוון רעיונות. יותר מכך, נדרוש שיהיה פשוט לבטא בה עקרונות בסיסיים. יעילות: רקורסיה, למשל, היא ביטוי שימושי, אך לא תמיד יעיל. פשטות: מינימום רכיבים בסיסיים בשפה אותם יש ללמוד. עבור דרישה זו יש לעתים trade-off עם נוחות למשל, ב- C ישנם שלושה סוגי לולאות לצורך נוחות. אחידות ועקביות: אלמנטים שונים בשפה רצוי שיתנהגו עם חוקי תחביר דומים בהירות: תחביר השפה צריך להיות ברור לבני אדם כך שיהיה נוח לעבוד איתו. הסתרת מידע ומודולריות. בטיחות: איתור מספר רב ככל הניתן של שגיאות בזמן הידור ולא בזמן ריצה. 2.3. סיווג שפות תכנות לפי עקרונות תכנות: איך השפה מטפלת בערכים: values, types and expressions איך השפה בודקת את סוג המשתנים: typing systems איך השפה שומרת את הערכים: storage האמצעים של השפה לשמור את הערכים: commands ניהול זרימת התוכנית: sequencers קישור בין שמות לערכים: binding האפשרות ליצור מבני general-purpose בשפה: abstraction Imperative Programming (PASCAL, C) Encapsulation/OO (ADA/C++, Java) Functional Programming (ML, LISP) Logical Programming (PROLOG) Concurrent Programming (ADA, MODULA-2) לפי צורת התכנות בשפה: -6-
3. ערכים וסוגים 3.1. ערכים הגדרה: ערך (value) זהו כל דבר שניתן להעביר כארגומנט לשיגרה. נסווג ערכים בשתי דרכים: לפי המבנה שלהם - הדרך בה הם מוגדרים לפי הפונקציונליות שלהם - הדרכים בעזרתן אנו מבצעים מניפולציות על הערכים. 3.1.1. מבנה הערכים - Primitive value ערכים שלא מורכבים מערכים אחרים, ערכים "אמיתיים", כגון: אותיות, שלמים, ממשיים, מצביעים. - Composite value ערכים שמורכבים מערכים אחרים: רשומות, מערכים, קבוצות, קבצים וכו'. קבוצת הערכים החוקיים בשפה מוגדרת להיות כקבוצה האינדוקטיבית המוגדרת על ידי הטיפוסים הפרימיטיביים שלה, עם המנגנונים בשפה המאפשרים יצירת.Composite values 3.1.2. מניפולציות על ערכים מלבד העברת ערכים לשגרות, ניתן לבצע איתם לעתים פעולות נוספות: החזרת ערך משגרה. השמת ערך לתוך משתנה. שימוש בערך כדי ליצור.composite value הגדרה: ערך המאפשר את כל הפעולות הנ"ל נקרא ערך ממחלקה ראשונה. -7-
3.1.3. ערכים בשפת Pascal ערכים ממחלקה ראשונה: כל הטיפוסים הבסיסיים: ערכים בוליאניים, תווים,,enums שלמים, ממשיים ומצביעים. ערכים ממחלקות נמוכות - יכולים לעבור כפרמטרים, אבל לא ניתן לשמור אותם: משתני התייחסות,(references) פונקציות ופרוצדורות. ערכי - composite אינם ממחלקה ראשונה כי לא ניתן להחזירם: records, arrays, sets and.files גם C וגם פסקל לא יכולים ליצור מערך של משתני התייחסות. 3.1.4. ערכים בשפת ML כל הערכים הינם ערכים ממחלקה ראשונה. טיפוסים בסיסיים: ערכים בוליאניים, שלמים, ממשיים, מחרוזות. ערכי records, tuples (records w/o field names), constructions :composite (=tagged values), lists, arrays פונקציות משתני התייחסות הגדרה: ביטוי הוא חלק של תוכנית המפורש-מחושב לערך על ידי חישוב (בזמן ריצה או בזמן הידור). -8-
3.2. סוגי ערכים בשפת מכונה: כל הערכים הם רצפים של ביטים - אין סוגים שונים של ערכים. בשפת אסמבלר: קיימים שני סוגי ערכים: מידע וכתובות. המתכנת לא יכול להגדיר סוגי ערכים חדשים. בשפות עיליות: סוגים שונים של ערכים. הערכים קיימים בזמן הידור typing) (static ולעתים בזמן ריצה typing).(dynamic 3.2.1. סוגי ערכים כקבוצות של ערכים לכל סוג מתאימה קבוצת ערכים, אולם לא לכל קבוצת ערכים מתאים סוג. לפיכך: הגדרת קבוצת הסוגים היא החלטה הנובעת מאופי השפה אותה אנו מגדירים..primitive values סוג המכיל רק - Primitive type.composite סוג שיש בתוכו ערכי - Composite type - Recursive type סוג המכיל ערכים המכילים ערכים מאותו סוג (למשל רשימות). סוגים: 3.2.2. סוגים בעלי סדר Ordered type סוג עליו מוגדר יחס סדר מלא. נוח להשתמש בסוג זה בלולאות ובמשפטי תנאי בהם השוואה נדרשת. Unordered Types למשל, מספרים מרוכבים. Discrete Type סוג בדיד שהוא ordered type וכן קיימת התאמה חח"ע בינו לבין תת קבוצה כלשהי של המספרים השלמים. דוגמאות:.boolean, char, integer :Order indiscrete types למשל: מחרוזות, מספרים ממשיים. בשפת פסקל: רק ערכים בדידים יכולים לשמש ללולאות ולקביעת גבולות של מערך. כמו כן הצהרות case יכולות לקבל ערכים בדידים בלבד. -9-
Semi-Primitive Types.3.2.3 סוג 1: ערכים אטומיים, שלא ניתן לפצלם ליחידות קטנות יותר, אולם הם אינם ערכים בסיסיים שהוגדרו על ידי השפה. דוגמא: enums סוג 2: ערכים שניתן לפרקם ליחידות קטנות יותר, אולם הם נבנו כחלק מהשפה. לדוגמא, מחרוזות בחלק מהשפות. מחרוזות ניתנות לפיצול ליחידות קטנות יותר. 3.2.4. מחרוזות מחרוזת היא סדרה של תווים. הסוג מחרוזת הוא סוג שימושי ביותר המופיע בצורה זו או אחרת בכל השפות המודרניות. שאלות לגבי מחרוזות: האם מחרוזת היא ערך primitive או ערך?composite האם היא בעלת גודל מוגדר מראש או גודל משתנה? איזה פעולות השפה מגדירה על מחרוזות? שפה ML ADA, Pascal Algol-68 C Prolog סוג primitive מערך של תווים מערך של תווים שגודלו יכול להשתנות בזמן ריצה בעזרת מצביעים ניתן לממש מערכים שמתנהגים כמערכים דינמיים רשימה של תווים. ניתן לגשת ישירות רק לתו הראשון פעולות מותרות שוויון, שרשור, פירוק לרכיבים מוגדרות כחלק מהשפה. ניתן להשתמש בפעולות על מערכים. חסרון: כל הפעולות מוגדרות על מערכים בגודל קבוע. ניתן להשתמש בפעולות על מערכים. ניתן להשתמש בפעולות על מערכים. אין פעולות מוגדרות בשפה מלבד בחירת התא הראשון. -10-
Composite Types.3.2.5.composite וערכי primitives חדשים בעזרת composite מייצרים סוגי Type Operators דוגמאות: tuples, arrays, sets, lists, trees וכדו'. ניתן להבין ערכים אלו בעזרת מושגים מתורת הקבוצות: מכפלה קרטזית: רשומות ו- tuples. איחוד: משתני.unions,variant מיפוי: מערכים ופונקציות. קבוצות חזקה: set סגירות תחת אופרטור: סוגים רקורסיביים, מבני נתונים דינמיים. מכפלה קרטזית בהינתן שני סוגים ST, מספר האיברים במכפלה הינו המכפלה הקרטזית שלהם תוגדר על ידי {(, ), }. S T = x y x S y T ( ). # S T = # S # T בשפת,ML הסוג tuple מייצג את המכפלה הקרטזית. בשפות אחרות, records ו- structs ניתנים לתפיסה כמכפלה קרטזית. סוג מיוחד של מכפלה קרטזית הוא זה שבו כל איברי המכפלה נבחרים מאותה קבוצה ) homogenous n ( ) ( ) n S = S S... S, # S = # S n.(tuples לפי הגדרה זו, 0 S סוג זה נקרא Unit והוא: מכיל בדיוק איבר אחד מכפלה קרטזית בעלת 0 איברים. {{ }} = Unit. סוג זה איננו הקבוצה הריקה, אלא הוא מכיל מכפלה קרטזית ללא איברים. Unit מתאים לסוג unit בשפת ML ולסוג void ב- C. הקרדינליות של סוג זה היא 1, ולפיכך הוא לא באמת צריך להיות ערך בשפה (לא צריך עבורו מקום אכסון). אם נרצה, נוכל להסתכל על unit כאל מבנה ללא שדות. בשפת C, פונקציה המחזירה void איננה פונקציה שאינה מחזירה כלום, אלא היא פונקציה המחזירה רק תוצאה אחת.unit פונקציה שאיננה מקבלת פרמטרים יכולה להחשב כפונקציה המקבלת.unit -11-
בשפת void C איננו סוג רגיל. למרות שפונקציה יכולה להחזיר ולקבל,void אין דרך לשמור void בתוך משתנה. Disjoint Unions union ב- C זהו מבנה המכיל מספר סוגים. הערך השמור במבנה הוא אחד (ורק אחד) מבין סוגים אלה. פסקל מציע variant records המציגים פונקציונליות דומה. בשפת Java לא קיים טיפוס דומה, מכיוון שה- OO מכסה את הצורך מבנה כזה. מבנה כזה ממשש הרבה פעמים לצורך הגדרת מצביעים: מצביע אל סוג X יכול להכיל משתנה מסוג X, או 0, למשל כלומר משתנה המכיל NIL או רשומה כלשהי. שפת ML מציעה כלים דומים בעזרת המילה השמורה.datatype אחת מהבעיות ב- Unions Disjoint היא מקרה בו אנחנו שומרים במבנה נתונים מסוג אחד וניגשים אליו כאל משתנה מסוג אחר, ומקבלים למעשה זבל. הדרך לסמן איזה סוג כרגע נמצא במבנה נקראית.tagging בשפת ML מוגדר ה- tagging כחלק מהשפה, וכל הפעולות נעשות בביטחה אין אפשרות לגשת לשדה לא נכון. בשפת Pascal חובה להגדיר tag בתוך ה- record.varient המהדר יוצר את השדות המתאימים ב- record בהתאם לערך של ה- tag (רק הערך הרצוי קיים ברגע אחד נתון) אולם כאשר ניגשים אל האובייקט לא חובה לעשות בו שימוש. שפת Turbo-Pascal איננה מחייבת שימוש ב- tag. שפת C: כל האחריות לשימוש ב- union מוטלת על המתכנת. המהדר אינו מבצע שום בדיקה. :Units נתייחס אליהם כאל בחירה בין :enums מציג לנו דרך להתייחסות אל Tagging עם union לדוגמא, אל ה- enum הבא נוכל להתייחס כמו אל ה- union המופיע מיד אחריו: typedef enum Suit { diamond, heard, spade, clover } Suit; typedef union Suit { struct {} diamond; struct {} heart; struct {} spade; struct {} clover; } Suit; -12-
הסוג None סוג עם קרדינליות 0. לסוג זה אין ערכים חוקיים. לדוגמא, הפונקציה exit() ב- C שלעולם לא חוזרת היתה צריכה להחזיר none אם סוג זה היה מוגדר בשפה. ניתן ליצור סוג זה בשפה, למשל על ידי: typedef enum { } None; typedef union { } None; (ההחלטה האם הם יעברו הידור היא תלוית מהדר). 3.2.6. סוגים רקורסיביים סוג רקורסיבי הוא סוג שמוגדר במונחים של עצמו itself).(defined in terms of בשפות Ada/C/Pascal סוגים רקורסיביים צריכים להיות מוגדרים על ידי מצביעים. בשפת ML ניתן לבצע הגדרה ישירה, למשל: datatype intlist = nil cons of int * intlist; ערכים אפשריים ל- intlist : nil cons(11, nil) cons(1, cons(3, nil)) שפת ML מכילה כחלק מובנה מהשפה בנאי רשימות בשם.list פעולות בסיסיות המותרות על רשימה: בדיקת האם היא ריקה, בחירת הראש, בחירת הזנב. -13-
3.3. שימוש בערכים ערכים ממחלקה ראשונה וממחלקה שניה ערך שכל הפעולות מותרות עליו נקרא ערך ממחלקה ראשונה. בפסקל פונקציות ופרוצדורות הן ערכים ממחלקה שניה ניתן להעביר אותן כערכים, אולם לא ניתן להציב לתוכן ערך. בשפות רבות ADA Fortran, Pascal, ועוד, פונקציות הן ערכים ממחלקה שניה עקב בעיות מימוש. ערכים ממחלקה ראשונה וממחלקה שניה הם מקרים קיצוניים. בפועל יש לעתים חריגות. פסקל, למשל, לא מרשה לפונקציות להחזיר ערכי,composite בעוד C מסכימה להחזיר מבנים, אבל לא מערכים. The Type Completeness Principle אסור שפעולות כלשהן יוגבלו באופן שרירותי לפי סוג הערכים המעורבים. 3.3.1. ערכי Composite כערכים ממחלקה ראשונה אין מניעה עקרונית לקבוע ערכי composite כערכים ממחלקה ראשונה, אולם שיקולי מימוש מונעים זאת לעתים: פסקל: מסוגלת להעבירם כפרמטרים גם לפי ערך וגם לפי התייחסות הערכים ניתנים לשמירה במשתנים לא ניתן להגדיר ערכי Composite כקבועים. לא ניתן להחזיר ערכי.Composite -14-
C גירסה מקורית שמה לעצמה כמטרה עליונה לדאוג שלא יהיו עלויות נסתרות מהמתכנת. לא ניתן להעביר ערכי Composite כפרמטרים (היה ניתן להעביר רק.(by value מערכים הועברו על ידי מצביע לתחילת המערך. העברת מבנים לא היתה מוגדרת כראוי ניתן לשמור ערכי Composite כמשתנים. לא ניתן להגדיר ערכי Composite כערכים קבועים בעזרת המילה.const לא ניתן להחזיר ערכי.Composite ANSI C לא ניתן להעביר ערכי Composite כפרמטרים (היה ניתן להעביר רק.(by value union/struct יכולים להיות ערך מועבר וערך מוחזר מפונקציה. מערכים לא ניתן להעבירם לפונקציה או להחזירם. אפשרות זו לא שונתה מכיוון שנעשה שימוש רק בזהות בין מערכים למצביעים ולכן לא היה ניתן לשנות זאת. (אם נרצה, נוכל לשים מערך עטוף על ידי struct וככה להעבירו.(by-value const מגדיר עכשיו תאי זיכרון שהערך שלהם לא יכול להשתנות, ולא קבועים. 3.3.2. פונקציות כערכים ממחלקה ראשונה המבנה של שפות רבת מציב קשים בהתייחסות לפונקציות כאל ערכים ממחלקה ראשונה. שפות רבות מציעות אפשרות של פונקציות המקוננות אחת בתוך השניה. שילוב של אפשרות זו עם הגדרת פונקציות כערכים ממחלקה ראשונה יכולים לגרום לבעות שיתגלו רק בזמן ריצה. הפתרון על ידי הגבלות מלאכותיות: פונקציות מוגדרות כערך ממחלקה שניה.(PASCAL) מניעת כינון פונקציות (C). לפונקציות מכוננות אין גישה למשתנים החיצוניים (++C). ML מאפשרת רק משתנים גלובליים -15-
.3.4 ביטויים (Expressions) ביטוי הוא קטע תוכנית היכול להיות מפורש לערך. בלוקים היוצרים ביטויים: Literals Aggregates Constant and Variable Access "כלים" היוצרים ביטויים: ביטויי תנאי קריאות לפונקציות.3.3.3 קבוע (Literal) הסוג הפשוט ביותר של ביטוי. זהו ערך קבוע וידוע מסוג כלשהו. דוגמאות מפסקל: integer 234 real 3.142 character 'a' string 'Standard ML' 3.3.4. קבועים ומשתנים const PI = 3.142; var r : real; ביטוי חוקי הוא גם ביטוי הכולל משתנים וקבועים. לדוגמא בפסקל, יהיו ההגדרות הבאות: -16-
אז הביטוי 2*PI*r מערב גישה אל קבועים וגישה אל משתנים. Aggregates.3.3.5 Aggregates הם ביטויים המייצרים composite value מתוך הרכיבים שלהם. ערכי הרכיבים מזוהים על ידי חישוב ביטויי משנה. דוגמאות: שפת :ML Tuple: (a*2.0, b/2.0) Record: {y = thisyear + 1, m = Jan, d = 1} List: [31, if leap(thisyear) then 29 else 28, 31, 30, 31, 30, 31,31, 30, 31,30, 31] int a[] = { 1, 2, 3, 4, 5 }; struct mystruct x = {4, 5 }; שפת C: אתחול מערכים: אתחול מבנים: שפת :Pascal לא קיימים ביטויים מסוג.Aggregates שפת ++C: קיימים רק אם קיים constructor הממיר אל סוג המבנה המבוקש. -17-
3.3.6. ביטויי תנאי ביטויי תנאי הם ביטויים המורכבים ממספר ביטויי משנה, אשר לפי תנאי נבחר בדיוק אחד מהם. דוגמאות: שפת :C האופרטור :? :LISP.case :ML (cond שפות רבות אחרות, כגון pascal אינן כוללות ביטויי תנאי. 3.3.7. קריאות לפונקציות הביטוי הוא התוצאה של הפעלת הפונקציה על הארגומנט(ים). Func(Actual Parameter Expression(s)) ברוב השפות: Func חייב להיות מזהה. דוגמא נוספת: משפטי if שהם ערכים ממחלקה ראשונה (למשל בשפת :(ML (if then sin else cos) (x) אופרטורים בשפות שונות הם בד"כ קריאות לפונקציות, הנכתבים בצורת.infix שפת LISP ייחודית בנקודה זו, מכיוון שבה אין ביטויי,infix וכל הפעולות, כולל הבסיסיות, הן פעולות.prefix למשל (אלוהים ישמור), חיבור של שני מספרים בשפת LISP נראה כך: (3 +) 2 בשפות כגון ++C או ML בהן קיימת האפשרות ל- overloading,operator קיימת זהות מוחלטת בין הפעלת אופרטור לקריאה לפונקציה. -18-
3.5. מערכות סוגים בשפות תכנות סיווג בשפות תכנות זהו מנגנון שנועד לאסוף את הערכים לסוגים. מטרות מנגנון זה: לתאר את המידע בצורה יעילה למנוע פעולות לא הגיוניות בין סוגים שונים רכיבי המנגנון: שיוך סוג לכל ערך בדיקת החוקים: בהנתן ערך ופעולה בדיקה האם הפעולה חוקית על אותו ערך פענוח: בהינתן סוג האופרנדים, החלטה על סוג הפעולה וסוג התוצאה. 3.5.1. סיווג מערכת סוגים קיום: האם קיימת מערכת סוגים בשפה. שקילות בין סוגים: מתי סוג אחד יכל להחליף סוג אחר. חוזק מערכת הסוגים: באיזה קפדנות נאכפים החוקים. זמן הבדיקה: מתי נעשית בדיקת הסוגים, האם קיימים סוגים סטטיים ודינמיים? אחריות: מי מחליט על הסוגים: המתכנת או המהדר. גמישות: קיום סוגים פולימורפיים, הגבלות של מערכת הסוגים. -19-
3.5.2. קיום שפה יכולה להיות: Typed קבוצת הערכים מחולקת לקבוצות, עם התנהגות זהה פחות או יותר לגבי אותה פעולה עבור ערכם מקבוצה זו. רוב שפות התכנות שייכים לקבוצה זו, לדוגמא: Pascal,,C,++C ML ועוד. Untyped לכל ערך יש את הפעולות המותרות ואסורות עליו. התוצאה של פעולה תלויה בערך הספציפי המועבר אליה. דוגמא למשל זו השפה LISP בה כל הערכים הם מסוג -S Expression שזהו למעשה עץ בינארי. בשפה זו לגיטימיות הפעולות נקבעת לפי מבנה העץ הניתן לפעולה. Degenerate קבוצת הפעולות הנתמכת בשפה היא מוגבלת כזו כך שהשפה דורשת רק סוג אחד או קבוצה קטנה מאוד של סוגים. שפות סקריפטים הן לרוב מסוג זה. למשל בשפת BATCH של DOS קיים רק סוג אחד מחרוזות. בשפת C-Shell קיימים שני סוגים, מחרוזות ומספרים, וכן תמיכה מוגבלת במערכים. 3.5.3. שקילות בין סוגים Equivalence) (Type נניח שפעולה מצפה לקבל איבר מסוג T אולם מקבלת איבר מסוג 'T. האם זו שגיאה? לא, אם 'T הוא תת סוג של T. שני סוגים שכל אחד מהם הוא תת סוג של השני נקראים שקולים. מכיוון שהגדרת תת סוגים היא יותר עדינה מהגדרת סוגים, שפות רבות כוללות הגדרה של סוגים אולם אינן כוללות הגדרה של תת סוגים. -20-
.3.5.4 זהות מבנה Equivalence) (Structural בשפת :Algol-68 If T and T are both primitive, then T T only if they are identical. Otherwise If: T = A x B, T = A x B, or T = A + B, T = A + B or T = A + B, T = B + A, or T = A B, T = A B then and A A, B B T T נביט למשל בסוגים הרקורסיביים: T = Unit + (A x T) T = Unit + (A x T ) סוגים אלו הם זהים מבנית אם כי קשה לפעמים לאבחן זהות זו בסוגים רקורסיביים. נקודות הבדל בין שפות שונות לגבי זהות מבנית: האם מבנים דורשים זהות בשמות השדות כדי להיות זהים או רק בסוגם? האם מערכים צריכים להיות בגודל זהה כדי להחשב זהים?.3.5.5 זהות בשם הסוג - Equivalence Name בפשטות Equivalence Name אומר כי סוגים זהים אם יש להם אותו שם. T T iff T and T were defined in the same place הגדרה עבור Pascal ו- Ada : -21-
דוגמא 1: TYPE VAR T1 = File of Integer; T2 = File of Integer; f1: T1; f2: T2; Procedure p(var f: T1);... p(f1); (* Ok *) p(f2); (* Compilation error *) דוגמא 2: תוכנית ראשונה תוכנית שניה program p2(f) type T = file of Integer; var f: T; begin... read(f,...); (* Type error *)... end; program p1(f) type T = file of Integer; var f: T; begin... write(f,...);... end; לפי ההגדרה של פסקל, שתי תוכניות פסקל אינן יכולות לתקשר ביניהם דרך קבצים בעזרת סוג שמוגדר על ידי המשתמש. בפועל, רוב מימושי פסקל לא בודקים את הסוג עבור הקבצים, ומכאן שמימושים אלו של פסקל אינם.type-safe -22-
3.5.6. זהות הצהרתית T T only if T and T have the same declaration. מופיעה בגרסאות חדשות יותר של Pascal (החל מגרסת :(ANSI 1983 לפי זהות זאת: TYPE VAR T1 = File of Integer; T2 = File of Integer; f1: T1; f2: T2; Procedure p(var f: T1);... p(f1); (* Ok *) p(f2); (* Ok *) 3.5.7. תת סוגים ב- Pascal שפת Pascal מציגה סוג אחד של Sub-typing הנתון לפי ההגדרה הבאה: if T=[a..b] and T =[c..d] then T is a subtype of T iff T is a subrange of T. להגדרה זו ישנה מגבלה, והיא שאין דרך למנוע את ההגדרה במקרה הצורך, למשל: type age = 0..120; height = 0..250; יתקיים כי age הוא תת סוג של,height ואין באפשרותנו למנוע זאת. -23-
3.5.8. סוגים נגזרים ו- subtyping בשפת ADA סוגים נגזרים: type age = derived integer range 0..120; height = derived integer range 0..250; לפי הצהרה זו age ו- height הם אינם תתי סוגים אחד של השני, אלא של.integer Subtyping של סוגי primitive בעלי סדר: subtype Probability is Float range 0.0..1.0; Subtyping של מערכי סוגים: subtype String is array (Integer range <>) of Character; subtype String5 is String (1..5); Subtyping של מבנים: type Sex is (f,m); type Person (gender: Sex) is record end record; subtype Female is Person (gender => f); 3.5.9. חוזק מערכת הסוגים ML, Java :Strongly typed languages ועוד: o בלתי אפשרי לשבור את הזיקה בין ערך לבין הסוג שלו במסגרת השפה. o לא ניתן לבצע על ערך פעולה שלא מותרת עבור הסוג שלו. o תוצאה: מימוש הסוגים יכול להיות מוסתר מהמתכנת על ידי השפה. C לדוגמא: :Weakly typed languages o לערכים יש סוג המתאים להם, אולם המתכנת יכול לשנות את הסוג או להתעלם ממנו. מצבי ביניים: Pascal o למשל הי שפה שהיא יותר strongly typed משפת C, עם חריגות בסגנון,variant records פרמטרים המועברים לפונקציות וקריאה מקבצים (בחלק מהמימושים). -24-
3.5.10. זמן הבדיקה שפת תכנות חייבת לוודא שלא תתבצע פעולה לא הגיונית. הבדיקה יכולה להתבצע בזמן הידור או בזמן ריצה. :Statically typed languages חוקי הסוגים נאכפים בזמן הידור. לכל משתנה וערך פורמלי מתאים סוג. שפות לדוגמא:,C Pascal, ML :Dynamically typed languages החוקים נאכפים רק בזמן רצה. למשתנים ולביטויים אין סוג משוייך בזמן ההידור, אלא רק לערכים יש סוג. הבדיקה צריכה להתבצע לפני כל פעולה: Smalltalk, prolog, APL ועוד. משתנים פולימורפיים יכולים להכיל ערך מכל סוג. שימושים למשתנים כאלה, הם למשל במקרים בהם הנתונים אינם מעוצבים בצורה אחידה ואיננו רוצים להגדיר מספר משתנים להחזקת הנתונים. לדוגמא: procedure ReadLiteral(var item); begin read a string of nonblanks; if the string constitutes an integer literal then item := numeric value of the string; else item := the string itself; end; חסרונות השימוש בסיווג דינמי: צריכה של יותר זכרון צריך לשמור לכל ערך את הסוג שלו בזמן ריצה. האטה של התוכנית צריך לנהל יותר מידע, צריך לנהל בדיקות בזמן ריצה. אבטחה הרבה באגים יכולים להתגלות על ידי בדיקת הסוגים בזמן הידור. בסיווג סטאטי לכל משתנה ולכל ביטוי משוייך סוג בזמן ההידור. לכל ביטוי מובטח בזמן ריצה שהפעולה מותרת עליו. בצורה זו המהדר יכול לעשות אופטימיזציות רבות על קוד. -25-
יתרונות הסיווג הסטאטי: אכיפת החלטות התכנון. מניעת קריסות בזמן ריצה. גילוי שגיאות מוקדם חוסך זמן פיתוח, עלויות ומאמצים. קוד יותר יעיל וקטן. מגבלות: חלק פעולות לא יכולות להיבדק בזמן הידור: o חלוקה ב- 0 לא יכולה להבדק (בעיה זהה לבדיקה האם תוכנית תסתיים). עם זאת איננו סובלים מירידה בביצועים בזמן ריצה על ידי שימוש ב- exceptions machine level במקרה זה. o פעולות חשבוניות מסוימות על ממשיים. ממומש על ידי בדיקה בקוד המיוצר. o חריגה מגבולות מערכים. סוגי אכיפה נוספים: :mixed typing languages שפות המשלבות static typing ו- typing.dynamic חלק מהחוקים נבדקים בזמן הידור וחלק בזמן ריצה. לדוגמא: בשפת Pascal רוב הפעולות נבדקות בזמן הידור, אולם בדיקת חריגה מגבולות של מערך נעשית בזמן ריצה..3.5.11 תת טיפוס type Sub טיפוס בטיפוס T הוא תת טיפוס של טיפוס T אם בכל מקום בו מוגדר להשתמש בטיפוס T ניתן להשתמש.T, T של הטיפוסים (structural) במקומו. ניתן להגדיר יחס תת טיפוס על סמך המבנה T הדוגמאות הבאות יבהירו את הנושא. 20..1, כי במקום מקום בו ניתן לקבל דוגמא 1 הטיפוס 10..1 הוא תת טיפוס של 1..20 ניתן גם לקבל 10..1. -26-
דוגמא 2 לטיפוס שהוא זוג struct) (tuple, של int ו- real (יסומן המכילה int ו- real. לדוגמא הטיפוס ( I R ניתן להגדיר כתת-טיפוס כל N -יה I R S נוכל לזרוק את האיברים המיותרים ב- N -יה. S כאשר מייצג.string האם לשדות צריך להיות אותו שם (במקרה של?(struct תלוי בשפה! כשנצטרך להשתמש ב- I R דוגמא 3 לטיפוס שהוא איחוד זר datatype) (variant record, של int, real, string (יסומן ב- I+R+S ) ניתן להגדיר כתת טיפוס כל טיפוס שהוא איחוד זר המכיל לפחות אחד מהאיברים,I R או S ולא מכיל איברים מיותרים. בכל מקום שבו מוגדר לקבל I+R+S נוכל לקבל I או R או S או I+R או I+S או.R+S אין בעיה להשתמש בטיפוס שקיבלנו במקום הטיפוס המקורי. A דוגמא 4 A (מערך, פונקציה) הסוג בהינתן מיפוי B B A הינו תת סוג אם: להיות מסוגלת B הוא תת סוג של B A הוא תת סוג של A! ההגיון כשמשתמשים ב- A במקום ב-, A על למפות לפחות את כל האיברים ש- A היתה מסוגלת למפות. לדוגמא:. g :1..8 7..8 g הפונקציה, f :1..5 6..9 עבור הפונקציה : f הבאה תהא תת סוג שלו: -27-
Arithmetic Types.3.5.12,AT(T) המכונה הסוג האריתמטי של הסוג T הינו מספר הערכים האפשריים עבור T. (כאשר מספר זה סופי). datatype color = r g b; למשל: עבור הסוג בשפת :ML מספר הערכים הוא 3. עבור מכפלה קרטזית של שני סוגים, הסוג האריתמטי של המכפלה הוא מכפלת הסוגים האריתמטיים של כל אחד מהסוגים. עבור union של סוגים, הסוג האריתמטי הוא סכום הסוגים האריתמטיים של כל אחד מהסוגים. A. B הסוג האריתמטי שלה הוא, f : A B, B עבור פונקציה המעבירה איברים מסוג A לסוג נסמן: AT(real) = R AT(string) = S עבור הסוג הבא: datatype 'a ptr = Null Something of 'a; הסוג האריתמטי של real ptr יהיה:.AT(real ptr) = 1 + R נגדיר סוג שהוא רשימה מקושרת: datatype real_list = nil Cons of real*real_list; נביט בערכים האפשריים שסוג זה יכול לקבל: Nil)), Nil, Cons(x1, Nil), Cons(x1, Cons(x2, -28-
( _ ) = 1 + ( _ ) AT real list R AT real list סוג זה הוא סוג רקורסיבי. המשוואה הרקורסיבית המתאימה לו הינה: ( ) ( ) ( ) נפתור את המשוואה: ( ) 2 AT real _ list = 1 + R AT real _ list = 1+ R 1 + R AT real _ list = 1 + R + R +... AT ( real _ list ) = R i= 0 i ומכאן: טיפוסים מנוונים טיפוס יקרה טיפוס מנוון אם לפונקציה היוצרת שלו קיים רכיב עם מעלה אינסופית. ניתן לאמר במקרה כזה כי לא קיימת פונקציה יוצרת לטיפוס. לסוג מנוון יש אינסוף קונפיגורציות אפשריות להחזיק את הטיפוס. ( 1 ) דוגמא לנוסחה עבור טיפוס מנוון:. L = + L + R נוסחה זו אינה מתכנסת. עקב ה- 1 המופיע במשוואה, המקדם של האיבר במעלה 0 אינו מוגדר. נשים לב שרשימות מקושרות אינן סוג מנוון. נכון שיש להן אינסוף ערכים, אולם המקדם של כל איבר הוא סופי. datatype 'a ptr = Null Full of 'a; דוגמא: נתונה ההגדרה הבאה בשפת :ML נבדוק האם מספר טיפוסים הם מנוונים: a' list ptr הטיפוס אינו מנוון. כדי לבדוק זאת נכתוב את הטיפוס האריתמטי: ( ). AT 'a list ptr 1 האיבר יכול להיות או NULL או רשימה. = + i= 0 R i ( ) = ( + a) ' 1. AT 'a ptr list לא ניתן i= 0 i הטיפוס מנוון. הטיפוס האריתמטי הינו להגדיר את המקדם של האיבר ממעלה 0 כי הוא תלוי ב- i. מכאן הסוג מנוון. ( ) ( R) לא מנוון. + 1 1+ = ptr AT 'a ptr 'a ptr list 'a ptr ptr -29-
Polymorphism.4 קיימות שתי גישות עיקריות: מונומורפיזם: לכל ביטוי יש בדיוק ערך יחיד. 1. פולימורפיזם: לחלק מהביטויים יש יותר מערך אחד. 2. הביטויים הפולימורפיים מתחלקים בעצמם למספר סוגים: overloading, coercion :Ad Hoc polymorphism parametric, inheritance :Universal polymorphism נפרט על סוגים אלה בהמשך. 4.1. מונומורפיזם לכל ביטוי שמתאים לו סוג, קיים סוג יחיד. דוגמאות: ליטרלים, קבועים, משתנים ופרמטרים. מערכת הסוגים בפסקל היא בגדול מונומורפית: פסקל מכריחה אותנו להגדיר את הסוג של כל פרמטר פורמלי, ואת סוג הערך המוחזר על ידי כל פונקציה. עם זאת, פסקל איננה לגמרי מונומורפית. תכונות פולימורפיות של פסקל: 1. קיים הקבטע nil משתנה המסוגל להצביע לכל סוג. 2. קיימות מספר פונקציות build-in בשפה ומספר אופרטורים הפועלים על מספר סוגים, ולכן בעלי סוג שאינו מסוגל להיות מוגדר על ידי מערכת הסוגים של השפה. 3. האפשרות ל- subrange מאפשרת ירושה. למשל, כל פונקציה המקבלת integer תקבל גם פרמטר מסוג,size אם size יוגדר בצורה הבאה: type size = 28..31; -30-
4.2. סוגים של פולימורפיזם :overloading מזהה יחיד מייצג מספר אבסטרקטיות בו זמנית. :coercion אבסטרקציה יחידה מסוגלת לשרת מספר סוגים על ידי כפייה מרומזת (הרחבת השימוש באבסטרקציה יחידה על ידי המרות סוגים). :parametric אבסטרקציות הפועלות בצורה אחידה על ערכים מסוגים שונים. :inheritance תת סוגים היורשים פעולות מסוג ההורה. Universal Polymorphism מול Ad Hoc Polymorphism.4.2.1 Universal הפולימורפיזם הוא מעל מספר אינסופי של סוגים הצורות השונות של הפולימורפיזם נוצרות באופן אוטומטי. קיים בסיס אחד משותף ואחיד שממנו כל הצורות של הפולימורפיזם לוקחות צורה. Ad hoc פולימורפיזם מעל מספר סופי של צורות לרוב, מספר צורות בודדות. צורות שונות של פולימורפיזם מקודדות באופן ידני או פסדו-ידני. אין בסיס משותף אחד לכל צורות הפולימורפיזם, מלבד זה שבכוונת המתכנת. צורות שונות יכולות להתנהג בצורה שונה. -31-
4.3. מערכות מונומורפיות מול מערכות פולימורפיות מערכות סוגים מונומורפיות ממומשות בשפות תכנות קלאסיות, למשל.Pascal כל אלמנט חייב להיות בעל סוג מוגדר. יתרון: קיימת בדיקת סוגים. חסרון: הוכח כי האפשרות לרשום קטעי קוד לשימוש חוזר היא מוגבלת: אלגוריתמים סטנדרטיים רבים, כדוגמת מיון, הם בעלי טיפוס כללי. מבני נתונים רבים, כגון רשימות או עצים, שומרים מידע כללי. מערכות סוגים פולימורפיות מופיעות בשפות מודרניות, לדוגמא ++C ושפת ML רכיבים בשפה יכולים להיות בעלי מספר סוגים קוד ניתן לשימוש חוזר תודות לפולימורפיזם דגש: נשים לב ש- overloading ו- coercion אינם הופכים מערכת סוגים לפולימורפית. הגדרה: קוד פולימורפי הוא קוד הרץ על משתנים מסוגים שונים. שפות שהן dynamic typed languages הקוד בהן הוא פולימורפי כמעט על פי הגדרה. שפות שהן statically typed systems הקוד מוגבל להגדרת הסוגים מלכתחילה. שפות אלו מציבות אתגר במימוש פולימורפיזם: אתגר על עוצמת הביטוי של הפולימורפיזם הניתן לביטוי בהן, ועל הבטיחות של השימוש בו. -32-
Overloading.4.3.1 העמסה: נתינה של יותר ממשמעות אחת למושג. המשמעויות השונות יכולות להיות, אולם אינן חייבות, להיות קשורות. דוגמא למשל: למילה static ב-++ C/C יש מספר משמעויות, בהתאם להקשר בו אנחנו משתמשים בה. נאמר שאופרטור הוא מועמס אם האופרטור מייצג שתיים או יותר פונקציות שונות. ב- C Pascal, ו- ML רק אופרטורים ומזהים הנמצאים build-in בשפה הם אופרטורים מועמסים. ++C לעומתם מכילה מנגנון המאפשר למתכנת לחפוף אופרטורים (כמעט) כרצונו. Overloading תלוי הקשר העמסה יכולה להיות תלוית הקשר או לא תלוית הקשר: תהי הפונקציה Id(E) כאשר Id הינה בו זמנית: A function f1 of type S1 T1 A function f2 of type S2 T2 בשפת :C++ :Context-independent overloading הפונקציה הנקראת נקבעת לפי הפרמטרים האקטואליים בלבד. אם E הוא מסוג S1 תיקרא f1 ואם E הוא מסוג S2 תיקרא f2. בשפת Context-dependent overloading :Ada הפונקציה מזוהה גם על ידי הפרמטרים האקטואליים שלה וגם על ידי ההקשר למקום בו היא נמצאת. כלומר, S1 ו- S2 צריכים להיות ברורים, או T1 ו- T2 צריכים להיות ברורים. Coercions.4.3.2 Coercion זהו מיפוי שקוף של ערכים מסוג אחד אל ערכים מסוג אחר. לדוגמא, Pascal מבצעת מיפוי שקוף של ערכים מסוג integer לסוג.real הפונקציה sqrt(n) המיועדת לערכים מסוג real תעבוד גם כאשר n הוא מסוג.integer שפות מודרניות מנסות לצמצם או להמנע לגמרי מהשימוש בתכונה זו. -33-
השימוש ב- Coercion יוצר לעתים דו משמעות. לעתים נדרשות מספר המרות כדי להמיר מסוג אחד לאחר. המסלול של ההמרה יכול להשפיע על התוצאה הסופית. מסלול ההמרה אינו עץ ואף אינו בהכרח גרף מכוון. המצב מסתבך עוד יותר כאשר מערבים coercion עם.overloading מה יקרה, למשל, כאשר מנסים לחבר ערכים מסוגים שונים? ב-++ C למשל, מיושמת הגישה הבאה: בהינתן פונקציה an) F(a1,,a2, יתכנו מספר רב של גרסאות מועמסות של F. ++C בוחרת בזמן הידור את הפונקציה המתאימה, לפי פרמטרים שונים: מקרים בהם אין צורך בהמרה או קיים צורך בלתי נמנע בהמרה:. array pointer, T const T. short int,float double. double int,int double,derived base,... הגדלת גודל הערך: המרות סטנדרטיות: המרות שהוגדרו על ידי המשתמש. הבחירה בין האפשרויות השונות נעשית על ידי "תחרות" בין הפונקציות. המנצח חייב להיות: מתאים יותר מהאחרים בלפחות פרמטר אחד, ולפחות טוב כמו האחרים בשאר הפרמטרים. אם לא קיים מנצח, ++C תדווח על שגיאה. Parametric Polymorphism.4.3.3 פולימורפיזם זה קורה עבור מספר אינסופי של סוגים קשורים. פולימורפיזם זה מאפשר להגדיר פעולה מופשטת שתפעל באופן אחיד על ארגומנטים שכולם ממשפחת סוגים דומה. -34-
:Parametric לעומת Ad hoc :Overloading פעולה מינימלית למספר קטן של הפשטות קיים מזהה זהה. אינו מגביר את יכולת הביטוי של השפה ניתן להיפטר מ- overloading על ידי נתינת שמות שונות להפשטות השונות המתאימות לו. הקשר בין ההפשטות השונות לא בהכרח קיים. :Coercion פעולה גדולה יותר אותה פונקציה יכולה לשמש למספר מטרות. עם זאת מספר המטרות הינו מוגבל, והערך המוחזר הינו זהה. הקשר בין הצורות מוכתב על ידי ה- coercions, וקשר זה אינו חלק מהשגרה. (universal) Polymorphic Type להפשטה יחידה יש מספר גדול של משפחות של סוגים קשורים. ההפשטה פועלת באופן אחיד על הארגומנטים שלה ללא תלות בסוגם. מוסיפה כוח ביטוי לשפה על ידי יצירת פונקציות היכולות לקבל מספר לא מוגבל של סוגים. :Parametric Type Polymorphism In Pascal האופרטורים - *,,+, פעולות איחוד וחיתוך קבוצות הינם כולם.Parametric Type Polymorphism הפרוצדורות new, dispose יוצרות משתנה מסוג כלשהו. הערך nil הוא ערך מצביע לסוג כלשהו. [] הקבוצה הריקה מתאימה לכל סוג. :Universal pointer in C בשפת C מצביע מסוג void* יכול להיות מושם לתוך כל מצביע אחר, וכן כל מצביע יכול להיות מושם בתוך משתנה מסוג.void* עובדה זו ב- C איננה ad hoc אלא parametric,polymorphism מכיוון שהיא מוגדרת אוטומטית לכל מצביע..parametric polymorphism לממש C++ זוהי הדרך של שפת Templates :C++ Templates בעזרת התבניות אנחנו מסוגלים ליצור פונקציות/מחלקות המקבלות פרמטר מסוג כלשהו שיוגדר בהמשך, לפי הצורך. הבדלים בין type variables ל- templates תזכורת: type variables (משתני סוג) הם סוג משתנה המייצג משתנה מסוג כללי כלשהו. דוגמא: נניח ש- Pascal היתה מאפשרת פונקציות פולימורפיות עם משתני סוג, זה היה נראה כך: function disjoint(s1, s2 : set of τ ) : boolean; begin disjoint := (s1 * s2 = [] ); end; כאשר τ הוא משתנה הסוג במקרה זה. -35-
תבניות מערבות ניתוח מאקרו: כאשר משתמשים בתבנית מושם הסוג בתוך התבנית ואז מבוצע פירוש של הסוג. במידה והבאנו סוגים לא חוקיים (למשל עבור הפונקציה max הבאנו ערכים מסוג struct כאשר הפונקציה ציפתה לערכים שניתן להשוות ביניהם), השגיאה תתגלה בזמן קומפילציה, אולם השגיאה תתגלה בגוף הפונקציה, ולא בפרמטרים שלה. לעומת זאת, בשפות התומכות ב- variables type כגון,ML האופי הפולימורפי של הפונקציה נרמז מתוך הגדרתה, ולכן הודעת השגיאה תתגלה עם הצהרת הפונקציה, או מיד בקריאה לה, ולא בתוכה. יתרה מזאת: C/C++ אינן מאפשרות לפונקציה להחזיר ערך שהוא פונקציה אחרת, למשל הרכבה. עם זאת, בהינתן תבניות, אפשרות זו היא אפשרות שהיינו מצפים לאפשר. המסקנה: שימוש ב- variables type הינו דרך הולמת יותר להשגת פולימורפיזם. :ML בשפת Parametric Polymorphism הפונקציות "fun second (x, y) = y;" :second היא פונקציה מהסוג:. α β β פונקצית הזהות המקבלת איבר כללי: ";x."fun id(x) = Polytypes.4.3.4 דוגמא: סוג מהצורה: σ τ τ נקרא polytype כאשר σ, τ הם משתני סוג. הגדרה: polytype זהו סוג המכיל בתוכו אחד או יותר משתני סוג. ( ) ( ) ( ). List τ, List τ τ, List τ Integer, דוגמאות נוספות: τ τ.parametric types נקראים גם polytypes Polytype יחיד יוצר למעשה משפחה שלמה של סוגים. למשל ה- polytype τ τ יוצר את String String,int int וכו'..monotypes בשפות מונומורפיות כל הסוגים הם.type variables זהו סוג שאינו מכיל monotype בשפות מונומורפיות, רק סוגים build-in של השפה יכולים להיות,polytype לדוגמא ב- Pascal :. file of τ המתכנת ב- Pascal איננו יכול להגדיר Polytypes חדשים (אם כי הוא יכול להגדיר סוגים חדשים). -36-
4.3.5. הערכים של Polytype נתעניין בקבוצת הערכים ש- polytype יכול לקבל. בשפת ++C: ל- templates אין ערך. רק כאשר אנחנו מספקים לתבנית את הסוג שתעבוד עימו, אנחנו יוצרים סוג אמיתי. בשפת :ML ניתן להגדיר את סוג ה- polytype. לדוגמא, הסוג של הפונקציה second הוא. α β β הגדרה: קבוצת הערכים שסוג פולימורפי יכול לקבל היא החיתוך של כל הסוגים שהוא יכול להגזר מהם. ההגיון: נניח ש- v הוא אחד מערכי ה- polytype. הפעולות היחידות שמותרות על v צריכות להיות הפעולות שזמינות לכל אחת מה- monotypes הנגזרים מה- polytype. דוגמא: קבוצת הערכים של ) τ List( : List( τ ) הנגזרים מ- Monotypes הסוג :List(Integer) הכולל את כל הרשימות הסופיות של מספרים שלמים, כולל הרשימה הריקה. הסוג :List(Truth-Values) הכולל את כל הרשימות הסופיות של ערכי אמת, כולל הרשימה הריקה. הסוג :List(Strings) הכולל את כל הרשימות הסופיות של מחרוזות, כולל הרשימה הריקה. וכך הלאה. האיבר המשותף: לכל סוגים אלו ולאחרים הנגזרים מ- ) τ List( קיים רק איבר אחד בודד משותף: הרשימה הריקה. לכל רשימה לא ריקה קיים monotype המתאים לה, בהתאם לסוג האיברים שבה. בצורה דומה, nil זהו הערך היחיד של. Pointer( τ ) דוגמא: קבוצת הערכים של τ τ : τ τ הנגזרים מ- Monotypes הסוג : Integer Integer הכולל את פונקצית הזהות, פונקצית העוקב, פונקצית הערך המוחלט ועוד. : String String הכולל את פונקצית הזהות, פונקצית הפיכת המחרוזת, פונקצית האורך ועוד. : Truth Values Truth Values הכולל את פונקצית הזהות, הפעולות הלוגיות ועוד. הסוג הסוג פונקצית הזהות היא היחידה המשותפת לכל הסוגים. -37-
( ) Pair τ = τ τ יכול להיות גם בן 0 או ערכים. למשל, ל- polytype קיימים 0 ערכים. Pair ( τ ) = τ τ ( ) List ( τ ) = Unit + τ List ( τ) Array ( σ, τ) = σ τ Set ( σ ) = ( σ ) סימונים לכמה polytype נפוצים: 4.3.6. משמעות ל- polytype ה- polytype של פונקציה מוסר לנו לרוב מידע רב על מה שהיא עושה. במקרים רבים, קל לנחש מה הפונקציה עושה רק מלהסתכל ב- polytype. ( ) ( ) ( ) ( ) Integer, List τ τ, List τ List τ, List τ ועוד. דוגמאות: τ τ Inclusion Polymorphism.4.3.7 Inclusion Polymorphism זהו הסוג השני של.Universal Polymorphism סוג זה נובע מקשרי הכללה בין סוגים או קבוצות של ערכים. :Subtype. A הגדרה 1: הסוג A הוא תת סוג של B אם B הגדרה 2: הסוג A הוא תת סוג של B אם כל ערך ב- A יכול להיות מומר (coerced) לערך ב- B. דגש: תת סוג איננו סוג ערך יכול להיות שייך לסוג אחד בלבד, אולם ערך יכול להיות שייך למספר תתי סוגים. רוב ה- Polymorphism Inclusion נובעים מ- subtypes. דוגמאות: :Pascal הערך nil שייך לכל סוגי המצביעים. C: הערך 0 הוא פולימורפי שייך לכל סוגי המצביעים. ++C: הסוג void* הוא סוג-על לכל סוגי המצביעים. -38-
Storage.5 משתנה הוא יישות המכילה ערך. הערך יכול להיות ערך primitive או.composite שתי הפעולות הבסיסיות המוגדרות עבור משתנה הן: קבלת ערכו, ועדכון הערך שלו. משתנים בשפות אימפרטיביות מול משתנים מתמטיים: משתנים מתמטיים מייצגים ערכים קבועים אולם לא ידועים. משתנים בשפות אימפרטיביות יכולים לשנות את הערך שלהם במהלך הזמן. משתנים יכולים להיות קצרי חיים: חיים רק במהלך ריצת התוכנית (או חלק ממנה) או בעלי חיים ארוכים: קיימים בלי קשר לתוכניות בקבצים ובבסיסי נתונים. ניתן למיין משתנים לפי המקום בו הם שמורים: medium),(storage למשל: הזיכרון, הדיסק וכו'. מודל לתיאור אכסון מידע: שטח האכסון (Store) מכיל אוסף של תאים. כל תא יכול להיות מוקצה או לא מוקצה. לכל תא יש תוכן.(undefined) או לא מוגדר (storable value) התוכן יכול להיות ערך כלשהו (content) דוגמא: var n: Integer; { Some unallocated cell changes status to allocated } begin n := 1 { Content changes from undefined to 1 } n := n + 1; { Content changes from 1 to 2 } end; { Cells changes status to unallocated } Composite Variables.5.1 Composite Value מכיל רכיבים שהם ערכים. ניתן לגשת אל הערכים באופן סלקטיבי. Composite Variable מכיל רכיבים שהם משתנים. ניתן לגשת וגם, לפחות באופן עקרוני, לעדכן את ערכים אלו בנפרד. -39-
.5.1.1 המבנה של Composite Variables ברוב השפות: המשתנה מהסוג T בנוי כמו הערך של הסוג T. חריגות: למשל: לא ניתן לגשת אל packed array ב- Pascal לפני שמבצעים unpacked עליהם. משתנים מסוג מבנים (records) בנויים כ- tuples של משתנים: למשל: type Date = record m: Month; d: 1..31 end; var today: Date;. אזי המשתנה today יראה כך: משתנה מסוג מערך יראה לרוב כרצף של משתנים. Array Variables.5.1.2 מערך הוא מיפוי מקבוצת אינדקסים אל אוסף של משתנים. קבוצת האינדקסים נקבעת בזמנים שונים, בהתאם לאופי המערך: Static Arrays הגודל נקבע בזמן הידור. Dynamic Arrays הגודל נקבע בזמן ריצה, ברגע הקצאת הזיכרון. Flexible Arrays הגודל לא מוגדר. הגבולות יכולים להשתנות בזמן הריצה. הסוג של flexile and dynamic arrays הינו ( ). Integer Integer Integer τ ה- Integer הראשון הוא הגבול התחתון של המערך, השני הוא הגבול העליון. ה- Integer השלישי הוא האינדקס אליו ניגשים והאחרון זה הערך המוחזר. -40-
Storables.5.1.3 :Storables ערכים שניתן לשמור אותם בתא בודד, ולא ניתן לעדכן חלקים מהם באופן סלקטיבי. "אטומים" של יחידות אכסון. לדוגמא, ה- storables של :Pascal primitive types sets לא ניתן לעדכן איברים בהם באופן סלקטיבי. מצביעים :Pascal non-storables מערכים, רשומות וקבצים ניתנים לעדכון בצורה סלקטיבית. פרוצדורות, פונקציות, משתני התייחסות לא ניתנים לאכסון. בשפת :ML כל הערכים חוץ ממערכים הם.primitives, records, lists, functions, references :storables Lifetime.5.1.4 הגדרה: Lifetime של משתנה (תקופת החיים של המשתנה) הינה זמן בין היצירה (הקצאה) של המשתנה לבין השחרור שלו. החשיבות של ניהול תקופת החיים של משתנים היא לניהול אפקטיבי של הזיכרון. סוגי תקופות חיים: Block activation: local variables Program activation: global variables Programmer decision: heap variables Permanent: persistent variables Just once in a lifetime -41-
5.1.5. משתנים לוקליים וגלובליים משתנים לוקליים הם משתנים המוגדרים בתוך בלוק והמיועדים לשימוש בתוך אותו בלוק בלבד. משתנים גלובליים הם משתנים לוקליים המוקצים בבלוק החיצוני ביותר. :Block Activation פרק הזמן בו בלוק מסוים מורץ. משתנה בעל אותו שם בבלוק זה יכול להיות בפועל מספר משתנים שונים: בריצות שונות של הבלוק המשתנה נוצר כמה פעמים. כמו כן בקריאה רקורסיבית מספר יצירות בו זמנית של אותו שם משתנה יכולות להתרחש. משתנים סטטיים הם משתנים שתקופת החיים שלהם היא כתקופת החיים של כל התוכנית, ללא קשר למקום בו הם מוגדרים בפועל. השימוש שלהם הוא לצורך שמירת מידע שיש בו צורך במהלך הפעלות שונות של אותו הבלוק/מודול, עם כוונה להמעיט את השימוש במשתנים גלובליים. יש לציין שעל ידי שימוש ב- OOP הצורך במשתנים סטאטיים נעלם. משתני ערימה Variables) (Heap הם משתנים הנוצרים ונמחקים לפי רצון המתכנת, ללא גבולות מוגדרים על ידי השפה. קיימות פעולות בשפה ליצירת משתנים כאלה והריסתם. אל משתני ערימה נגשים באמצעות מצביעים או משתני התייחסות. הערה: משתני ערימה, בצירוף מצביעים, נחשבים ל- type.recursive סמנטיקת ערכים מול סמנטיקת משתני התייחסות.1 ל-"העתקה והשוואה" של מצביעים קיימות שתי משמעויות: משמעות לפי הערך: הערכים האקטואליים הם המועתקים והמושווים. כאשר מדובר על ערך שהוא composite ישנן שתי אפשרויות: :Deep semantics כל האובייקטים שניתן לגשת אליהם על ידי ערך זה מועתקים ומושווים. :Shallow semantics משווים/מעתיקים רק את האיבר הראשון..2 משמעות לפי משתנה ההתיחסות: רק משתנה ההתיחסות מועתק. -42-
להשוואה של מצביעים גם יש שתי משמעויות: משמעות לפי הערך: השוואה איטית. כל גרף הרכיבים הניתנים להשגה מושווים. משמעות לפי משתנה ההתיחסות: השוואה מהירה. בדיקה האם שני משתני ההתייחסות מצביעים אל אותו אובייקט. התפלגות השיטות בשפות השונות: Lisp, ML, Prolog :Value semantics languages Java, Smalltalk :Reference semantics languages Eiffel, C++ :Mixed semantics languages רוב השפות מערבות את השיטות. :Lazy copying טכניקה למימוש,value semantics לפיה העתקת הנתונים של אוביקט גדול נעשית על ידי,reference ואילו הנתונים הממשיים מועתקים אם וכאשר משתנה המקור או היעד משונים. Persistent Variables.5.2 קבצים הם.composite variables קבצים סדרתיים יכולים להחשב כסדרה של איברים, וקבצים עם גישה ישירה נחשבים כמערך של רכיבים. דוגמא נוספת: הפרמטרים שתוכנית בשפת פסקל מקבלת. משתנים אלה קיימים לאורך כל ריצת התוכנית, והמתכנת אינו יכול לשחררם או ליצור משתנים נוספים כאלו במהלך הריצה. תקופת החיים של משתנים כאלה מוגבלת על ידי ההפעלה של התוכנית שיצרה אותם. -43-
Dangling References.5.3 :Dangling Reference ניסיון לגישה אל משתנה שכבר לא קיים. תופעה זו קוראת במספר מקרים: מצב 1: כאשר מתבצע עירוב של משתני ערימה עם משתני מחסנית: מצביע הצביע לערך לוקלי. ביציאה מהפונקציה יתכן מצב בו המצביע נשאר קיים (המצביע לא היה משתנה לוקלי של אותה פונקציה) אולם הערך שהוא הצביע אליו כבר שוחרר. מצב 2: כאשר מנסים לגשת אל משתני ערימה ששוחררו כבר. פתרון לבעיות אלו יכול להיות הגדרת כל המשתנים כמשתנים גלובליים. כל השפות הפונקציונליות, וכן,prolog מאמצות את גישה זו. עם זאת, גישה זו איננה יעילה. פתרון נוסף לבעיה:.garbage collector ניהול הזכרון עובר לידי הסביבה, המטפלת בשחרור המשתנים כאשר אין בהם צורך יותר. במערכות המבוססות GC אין משתני מחסנית, כל המשתנים מוקצים על הערימה. לגישה זו מספר חסרונות. למשל התוכנית הבאה תרוץ לאט ב- Java אולם מהר ב-++ C/C : void f() { data a[10000]; } for (i = 0; i < 10000; i++) f(); פתרון לבעיה: קיים מנגנון המנסה לזהות איזה מהמשתנים הלוקליים יכול "לברוח" מהפונקציה ואיזה לא. משתנים שאינם יכולים לברוח, מוגדרים כמשתנים לוקליים. -44-
6. פקודות 6.1. אבני הבניין של השפה 6.1.1. פקודות פקודה היא קטע תוכנית המורץ כדי לעדכן משתנים. Skips.6.1.2 Skip זוהי פקודת דמה שלא מבצעת כלום. למשל, ; בשפת C. פקודת skip שימושית בעיקר במשפטי תנאי: if E then C else skip 6.1.3. סיום פקודה בשפת C כל פקודה מסתיימת בנקודה פסיק. בשפת Pascal ניסו לתת לשפה להראות דומה יותר לשפת בני אדם למשל אחרי תנאי if שלאחריו קיים,else לא שמים נקודה פסיק. במבחן התוצאה, התגלה כי למתכנתים יותר קל לכתוב בשפות בסגנון C. 6.1.4. הערות הערות הן אינן פקודות, אולם הן חלק מהשפה. קיימות שתי סוגי הערות: הערות על שורה בודדת והערות על בלוקים. לגבי בלוקים, השפה צריכה להגדיר האם הערות יכולות להיות מקוננות אחת בתוך השניה או לא. -45-
6.1.5. השמות המבנה הכללי של השמה הינו: ;E V =: בשפות בהן משתני התייחסות הם ערכים ממחלקה ראשונה, V יכול להיות ביטוי, לדוגמא: (if then m else n) := 7 ב- C צורת הכתיבה הנ"ל אסורה, אולם ב- GNU-C היא מותרת. השמות נגשות אל המשתנים. גישה למשתנים יכולה להיות גישה אל התוכן שלהם או התייחסות (reference) אליהם. ב- Pascal וב- C, המשמעות של הגישה נקבע לפי ההקשר. ב- ML המילה val מייצגת גישה אל התוכן, אחרת מדובר בגישה אל ההקשר. V := E; ML vs. Pascal ML both sides evaluated E evaluates to type V may be an expression V must evaluate to type ref ':=' is a function with side effect of type (type ref) type ( ) dereferencing, converting ref's to values, must be done explicitly: val n = ref 5; Pascal only RHS evaluates E evaluates to type V must be an identifier V is of type ':=' is a command variable access is context dependent 6.1.6. קריאות לפונקציות פקודת הקריאה לפונקציה מפעילה פונקציה על מספר ארגומנטים. הפרמטרים של הפונקציה הינם ביטויים או גישות למשתנים (תוכן או התייחסות). לכל פונקציה יש השפעה: שינוי משתני התייחסות שהועברו אליה, שינוי משתנים גלובליים, שינוי ערכים של משתנים לוקליים סטאטיים. -46-
6.2. בקרת זרימה Sequential and Collateral Commands.6.2.1 פקודות שהן.composite commands types מכילות מספר פקודות בתוכן. עבור Sequential command הסדר של הפקודות הוא חשוב. לדוגמא: ;C2 ;C1. עבור :Collateral command סדר הביצוע אינו משנה: n+1; m := 7, n := Conditional Commands.6.2.2 מתוך קבוצה של תתי פקודות, בדיוק אחת נבחרת לביצוע דוגמאות: if בשפת case C, בשפת.Pascal נשים לב כי הפקודה switch ב- C אינה כזו. 6.2.3. לולאות מחולקת ל- 2 : לולאות לא מובטחות :(indefinite) לא ניתן לדעת את מספר הפעמים שהלולאה תרוץ מראש. דוגמאות:.while, repeat until לולאות הרצות מספר קבוע של פעמים (definite) למשל for בשפת.Pascal Collateral Loops.6.2.4 לולאות מסוג זה הן לולאות שסדר ביצוע גוף הלולאה איננו חשוב. לולאות אלה שימושיות במחשבים בעלי ארכיטקטורה מקבילית. לולאות אלו שימושיות רב עבור לולאות.definite הפקודה for בשפת C הינה,indefinite ולכן נדיר למצוא גרסאות של C בארכיטקטורות מקביליות. -47-
6.2.5. משתני לולאה שפות תכנות נבדלות במשתני הלולאה במספר נקודות: מה ערך המשתנה במידה ויוצאים מהלולאה באמצע? מה ערך המשתנה לאחר סיום הלולאה? לא מוגדר לא מוגדר Pascal ידוע, לפי החוקים. ידוע, לפי החוקים. ++C המשתנה נשאר קיים לאחר סיום הלולאה. המשתנה לא קיים לאחר סיום המשתנה לא קיים Ada הלולאה. לאחר סיום הלולאה. האם גוף הלולאה יכול לשנות את משתנה הלולאה? בלתי אפשרי אפשרי בלתי אפשרי. Power Loops.6.2.6 Power Loops זוהי הכללה של לולאות מבנה איטרציה בו עומק הכינון (nesting) של הלולאה הוא משתנה. כלי זה עוזר במקרים רבים בהם היינו משתמשים בשפות מוכרות ב- back-tracking למשל, בעית 8 המלכות. variable Queen: array 1..n of integer; nest Column := 1 to n for Queen[Column] := 1 to n do if OkSoFar(Column) then deeper; end; end; do end; write(queen[1..n]); -48-
6.2.7. ביטויים עם side-effects ב- C ובשפות רבות אחרות לביטוי יכולים להיות side-effects כגון עדכון ערכי משתנים. הביטויים משמשים למעשה כסוג של פקודה. מכיוון שלפונקציות יכולים להיות,side-effects גם הביטויים (expressions) בשפת Pascal הינם פקודות (למרות שהשפה מנסה מאוד להמנע מכך). if (getchar(f) = 'F') gender = female; else if (getchar(f) = 'M') gender = male; Side-Effects יכולים לגרום במקרים רבים לבעיות, לדוגמא: שני תווים שונים נקראים עקב ה- side-effect של.getchar Expression Oriented Languages Expression Oriented Languages הן שפות בהן לא קיים הבדל בין ביטויים לבין פקודות. דוגמאות לשפות כאלה: ML, Icon, C ועוד. לרוב, הערך של השמה הוא הערך שההשמה הציבה במשתנה. יוצאת דופן היא שפת ML בה הערך של השמה הינו () ( n -יה בגודל 0). הערכים של לולאות לרוב הם ערכים ניטראליים כגון 0 או.NULL בשפות אלו סגנון התכנון מתבסס רבות על.side-effects תכנות מבני: תכנות המבוסס על פקודות המתבצעות ברצף, משפטי תנאי ולולאות. לכל רכיב תוכנית יש כניסה אחת ויציאה אחת. תוכנית הבנויה בתכנות מבני יכולה להיות מוצגת כתרשים זרימה. קיים אלגוריתם אשר בהינתן תוכנית כזו, מצייר את תרשים הזרימה המתאים. עם זאת, לא לכל תרשים זרימה מתאימה תוכנית מבנית. -49-
Sequencers.6.2.8 :Sequencer מבנה אשר מאפשר לשנות את זרימת הבקרה הרגילה של התוכנית, ועל ידי כך מאפשר ייצוג תרשימי זרימה נוספים על ידי קוד. Sequencer מאפשר מצב של כניסה יחידה לבלוק ויציאות מרובות. דוגמאות: קפיצות: מעבר מפורש של הבקרה מנקודה אחת לאחרת. :Escapes העברת הבקרה אל סוף הבלוק העוטף. :Exceptions העברת הבקרה ל- handler כאשר תנאי מסוים מתרחש. :Coroutine סיום השיגרה הנוכחית עם אפשרות לחזור אל אותו מקום בהמשך. קפיצות קפיצה היא העברה מפורשת של הבקרה מנקודה אחת בקוד לנקודה אחרת. בשפות רבות קיימת פקודת הקפיצה.GOTO קפיצות גורמות בד"כ ל-"קוד ספגטי". תוויות (labels) משמשות כדי לציין נקודות בתוכניות. Escapes בריחה היא סיום הרצת רכיב קוד. בריחה נחשבת לצורה מבנית הרבה יותר מקפיצה. דוגמאות מ- C : סיום לולאה: break סיום פונקציה: return סיום התוכנית: exit (למרות שזו אינה פקודה בשפה). שפות נוספות מציגות בריחה ממספר לולאות, כלומר, break מגבילה את המתכנת ליציאה מהלולאה הנוכחית. קיימות שפות התומכות בפקודת,break L כלומר, "סיים את הלולאה L", היכולה להמצא מספר רמות כינון מעלה. בצורה דומה, קיימת המילה continue מעבר לאיטרציה הבאה של הלולאה. ב- C זוהי רק הלולאה הנוכחית. ב- Perl לדוגמא זו יכולה להיות כל לולאה בדרך מעלה. -50-
Exceptions מקרים חריגים כגון חלוקה ב- 0, גישה למקום אסור בזיכרון, וכו', יכולים להתרחש. על תוכנית עמידה להגיב למקרים אלה. לרוב חלקים גדולים מתוכניות מוקדשים להתמודדות עם מקרי הקצה. הטיפול בחריגות מתחלק לגילוי, שנעשה בד"כ ברמת החומרה, לטיפול והתאוששות מהחריגה, שנעשה ברמת התוכנה, על ידי.handler סוגי טיפול בחריגות: Resumption המשכה כרגיל אחרי שה- handler מבצע את עבודתו. Explicit error handling הפונקציה מחזירה ערך שגיאה המציין שקראה תקלה ובאחרות הקורא לבדוק אותו. קפיצה לרמות גבוהות יותר בתוכנית להמשך הריצה משם. טיפול מיוחד, בהתאם לחריגה שקרתה. Coroutines שגרות רגילות מתחילות בנקודת הפתיחה שלהן, מתבצעות עד סיומן, או עד הגעה ל- return. הקריאה הבאה אליהם מתחילה שוב מההתחלה. Coroutine הן שגרות המסוגלות לעצור את הריצה שלהן ולחזור אל הקורא, ולהמשיך בפעם הבאה מהנקודה בה הן הפסיקו. שימוש לדוגמא: השוואת שני עצים בינריים על ידי מעבר רקורסיבי יחיד והשוואה בין העצים. -51-
Bindings.7 :Binding יכולת המתכנת לקשר בין מזהים לישויות. הישויות יכולות להיות קבועים, משתנים, פונקציות, סוגים ועוד. הצהרה (declaration) פעולת הקישור בין מזהה לישות. סביבת העבודה אוסף של קישורים.(bindings) כל פקודה וכל ביטוי מפוענחים על ידי סביבה מסויימת. כל המזהים בביטוי או בפקודה חייבים להיות מקושרים באותה סביבה. סביבה היא למעשה מיפוי חלקי מקבוצת המזהים אל קבוצים הישויות. Scope בהינתן הצהרה כלשהי d, נכנה בשם scope את קטע טקסט התוכנית בו הצהרה זו היא תקפה. בלוק (Block) קטע תוכנית החוסם את ה- scope של כל הצהרה שהוא מכיל בתוכו. Block structure שם לקשר טקסטואלי בין בלוקים. (האם בלוקים יכולים, למשל, להיות מקוננים?). Block structure.7.1 Monolithic Block Structure כל התוכנית הינה בלוק בודד. שפות ישנות, כגון Basic, Cobol משתמשות בשיטה זו. שיטה זו אינה אפקטיבית לפרויקטי תוכנה גדולים. Flat Block Structure התוכנית מחולקת לבלוקים. ההגדרות של כל בלוק הן מקומיות לו. כל הגדרה שאיננה בבלוק, היא הגדרה גלובלית. שפה לדוגמא הפועלת בשיטה זו היא.Fortran -52-
Nested Block Structure התוכנית מחולקת לבלוקים. ההגדרות של כל בלוק הן מקומיות לו. בלוקים יכולים להיות מקוננים. זוהי צורת העבודה הפופולרית ביותר בשפות התכנות הקיימות. Static and Dynamic Binding.7.2 const S = 2; Function scaled (d : integer) : integer; Begin scaled := d * S; End; Procedure P; const S = 3; Begin scaled(5); { Static binding: Value is 10 } { Dynamic binding: Value is 15 } End; Begin End. Scaled(5); { Value is 10 } ההבדל בין static binding ל- binding dynamic הינו בזמן הקישור בין הערך- ישות, לבין המזהה. :Static binding הקישור מתבצע בזמן ההידור. קיים ב- Ada,C Pascal, Algol, ועוד. השיטה היא למצוא את הבלוק הקטן ביותר המכיל את ההצהרה. Lisp הקישור מתבצע בזמן ריצה. שפות לדוגמא המשתמשות בקישור כזה הינן :Dynamic binding ו- Smalltalk. השיטה היא למצוא את הבלוק הפעיל האחרון שהצהיר על המזהה, ולהשתמש בערך זה. Dynamic binding יוצרת שגיאות זמן ריצה (סוגים לא מתאימים, או אפילו הצהרות חסרות בזמן הריצה). כמו כן קשה יותר להבין מה פונקציות עושות כאשר משתמשים ב- binding.dynamic -53-