(Go: >> BACK << -|- >> HOME <<)

לדלג לתוכן

אייפל (שפת תכנות)

מתוך ויקיפדיה, האנציקלופדיה החופשית

אייפל (Eiffel) היא שפת תכנות מונחה עצמים. שפה זו נוצרה בידי ברטנארד מאייר (שהיה חסיד של תכנות מונחה עצמים) ועל ידי חברת Eiffel Software ב-1985. ונקראה על שם גוסטב אייפל, המהנדס שתכנן את מגדל אייפל.

השפה נועדה לשפר שפות OOP קודמות. כתיבת השפה הושפעה מהשפות פסקל, Simula, ‏Ada‏, Z ושימשה השראה לשפות פופולריות כמו: C# ‏,Java ‏,Ruby‏ ,D ועוד. מושגים רבים שהוצגו לראשונה על ידי אייפל, לאחר מכן מצאו את דרכם לשפות תכנות מודרניות כ-#Java, C ושפות אחרות.

מאייר כתב ספר על תכנות מונחה עצמים (Object-Oriented Software Construction), בספר ניסח כללים לפרידגמה, שלבסוף הפכו לשפה עצמה.

מאפיינים עיקריים

  • יבילות - רץ על פלטפורמות שונות.
  • Melting ice - שילוב של הידור ומפרש שרץ ב-byte Code.
  • Design by contract - יצירת חוזה בעבודה עם קטעי קוד.
  • טיפוסיות חזקה (strong type) ללא המרות מרומזות.
  • קישור דינמי (dynaic bounding) בזמן ריצה. מאפשר פולימורפיזם.
  • קריאות - מנגנונים ללוגיקה, מבניות בקוד ותיעוד.
  • statically typed - מצריכה הגדרה בזמן הידור.
  • דוקומנטציה אוטומטית.
  • מחלקות גנריות - מאפשרת שימוש במחלקות גנאריות.
  • העמסת שיטות (Method overloading) - ניתן לבצע חפיפת מתודות של השפה.
  • העמסת אופרטורים (Operators overloading) - ניתן לבצע חפיפת אופרטורים של השפה.
  • Pointer Arithmetic לא קיים בשפה.
  • ביטויים רגולריים לא קיימיים בשפה.

עקרונות עיקריים

design by contract

"חוזה" הן למעשה קבוצה של התניות שמוגדרות על ידי המתכנת, המגדיר את כל הממשק וההשפעות של התוכנית על סביבתה, בכך ניתן להקל על כל מי שרוצה להשתמש בצורה הכי בטוחה והכי יעילה. העיקרון הומצא על ידי ממציא השפה וזאת אחת התכונות החשובות של השפה. לכל פונקציה ניתן להצהיר:

  • תנאים לפני הרצת הפונקציה (Pre Conditions)
  • תנאים לאחר הרצת הפונקציה (Post Conditions)
  • מגבלות לגבי תוכן הפרמטרים שהפונקציה מקבלת.
  • מגבלות לגבי התוכן שהפונקציה מחזירה.
  • מגבלות לגבי תוכן החריגות שהפונקציה יכולה לזרוק (exception)
  • תופעות לוואי של הרצת הפונקציה (לדוגמה שימוש בלולאה לצורך השהייה)
  • נתונים על הביצועים שלה (זיכרון, זמן ריצה)
  • ביטויים קבועים לאורך התוכנית (Invariant)

design by contract הינו סימן רשום של Eiffel software והמונח הוטמע והומצא על ידי ד"ר ברנרד מאייר יוצר השפה ולכן "החוזה" הינו חלק מאוד מרכזי בשפת אייפל.


דוגמה ל design by contract class DATE

 create
   make feature {NONE} 
   -- Initialization
   make (a_day: INTEGER; a_hour: INTEGER) -- Initialize `Current' with `a_day' and `a_hour'.
 
 require 
   valid_day: 1 <= a_day and a_day <= 31
   valid_hour: 0 <= a_hour and a_hour <= 23 
 do 
   day&nbsp;:= a_day
   hour&nbsp;:= a_hour
 
 ensure 
   day_set: day = a_day
   hour_set: hour = a_hour
 
 end
 
feature -- Access 
   day: INTEGER -- Day of month for `Current'
   hour: INTEGER -- Hour of day for `Current'
 
 feature -- Element change 
   set_day (a_day: INTEGER) -- Set `day' to `a_day'
 
require
   valid_argument: 1 <= a_day and a_day <= 31
 do
   day&nbsp;:= a_day
 ensure
   day_set: day = a_day
 end
 
 invariant 
   valid_day: 1 <= day and day <= 31
   valid_hour: 0 <= hour and hour <= 23
  end

Open Closed principle

קוד צריך להיות פתוח להרחבה וסגור לשינויים, והדבר יכול להתבטא בשתי צורות:

  1. בצורה שהדבר מתבטא בשפת אייפל - כותבים מחלקה בצורה רגילה, ואם מתגלה באג בתוכנה, במקום לשנות את המחלקה המקורית, יוצרים מחלקה חדשה שמשתמשת במחלקה הקודמת ועושה את השינויים שנצרכים, או שיוצרים מחלקה שיורשת מהמחלקה המקורית שבה עושים את השינויים.
  2. בצורה הרגילה שימוש במחלקה אבסטרקטית, וירושות ממנה בצורה כזאת שכולם צריכים להיות באותה התבנית, ואז מקשרים את כולם בפולימורפיזם.

uniform access principle

מכיוון שיש שינויים ותוספות של אובייקטים חדשים לשפה, דאגו לעשות שכל הפניות יהיו באותה צורה כדי לחסוך תקלות. ולכן יש מנגנון גישה זהה לכל הפונקציות והמחלקות. לדוגמה:

(Foo.bar(5

יכול לשמש בתור פונקציה שמקבלת את הערך 5 ויכול גם לשמש בתור מחלקה שהמשתנה שלה יקבל את הערך 5.

Command Quiery seperetion

הפרדה מוחלטת בין פונקציה שמחזירה ערך, כלומר בקשת מידע ואשר אינה יכולה בשום אופן לעדכן מידע, לבין פרוצדורה המעדכנת או מבצעת פעולה, אך אינה מחזירה מידע. וזאת על מנת לא ליצור side effects שלא שהמתכנת לא התכוון אליהם.

תאימות לשפות אחרות

השפה בנויה כך שתוכל להתממשק בקלות עם מגוון שפות (בדגש על שפת C ודוט נט) אפשר אפילו לכתוב קוד בשפת C בגוף הפונקציה של Eiffel, ובכך להשלים כל חיסרון שעלול להיות בשפה עם הסיפריות של שפת C.

ניהול זיכרון

ניהול זיכרון של תוכנית בשפת Eiffel הינה משימה לא קלה בגלל שלוש סיבות:

א. תוכנית בשפת Eiffel יכולה לקרוא לפונקציות בשפת C, לכן צריך להתחשב בכך שיכולות להיות בתוכנית 2 סוגי זיכרון: זיכרון ל-Eiffel וזיכרון ל-C.

ב. צריך להבחין בין אובייקטים רגילים - אובייקטים בעלי גודל קבוע שנקבע באופן סטטי (בזמן ההידור) על פי מספר תכונות האובייקט - לבין אובייקטים מיוחדים - אובייקטים בעלי גודל משתנה (מערכים, מחרוזות ועוד.).

ג. לא מספיק לשחרר זיכרון רק לשם שימוש חוזר בתוכניות בשפת Eiffel, אלא אנו מעוניינים שניתן יהיה להחזיר את הזיכרון אל מערכת ההפעלה לשימוש חוזר גם באפליקציות אחרות.

מסיבות אלה, Eiffel אינה מסתפקת ב- syscall malloc() (שכן malloc() לא יכול להחזיר את הזיכרון אל מערכת ההפעלה), אלא יש לה מנגנון לניהול זיכרון משלה.

כמו כן, על מנת לפתור את בעיית ה-Garbage Collection, השפה אינה מסתפקת באלגוריתם מוכן לביצוע Garbage Collection, אלא היא משתמשת בכמה אלגוריתמים בסיסיים, ובכל פעם שיש צורך ב-GC, האלגוריתם נקבע לפי הנסיבות ורמת הדחיפות.

שתי האלגוריתמים העיקריים למימוש GC בשפת Eiffel, הם:

  • Mark & Sweep
  • Memory Compaction

כברירת מחדל, כל יישום בשפת Eiffel כולל GC אוטומטי. אבל בכל אופן ניתן לשלוט על מנגנון ניהול זיכרון ולכוון אותו כך שיתאים לצרכים הספציפיים של המתכנת באמצעות פונקציות ופרוצדורות השייכות למחלקה MEMORY, שמאפשרים "להדליק" או "לכבות" את ה GC האוטומטי.

המאפיין הייחודי של ה-GC של Eiffel הוא שה-GC אינו מבצע שחרור זיכרון רק עבור שימוש חוזר על ידי הקצאת אובייקטים נוספים לאותה תוכנית, אלא למעשה, מחזיר את הזיכרון למערכת ההפעלה ומשחרר אותו לשימוש חוזר על ידי תוכנית אחרת. למרות שקשה ליישם את המאפיין החשוב הזה, הוא הכרחי בעיקר למערכות שצריכות לרוץ במשך הרבה זמן או אפילו באופן קבוע.

מבנה השפה

משתנים פשוטים

נקראים גם EXPANDED

הגדרת משתנים נעשית בדרך דומה לשפת פסקל: שם המשתנה נכתב מצד שמאל לאחריו נקודתיים ושם הטיפוס.

flag: BOOLEAN

i: INTEGER

s: STRING

r: REAL

d: DOUBLE

c: CHARACTER

אובייקטים

נקראים גם REFERENCE ומכילים אובייקטים או מערכים. לדוגמה אובייקט שאני הגדרתי (אלא אם כן הגדרתי במפורש EXPEND):

i: MYoBJ

ערכים דפולטיבים

ערך משתנה
1 0 INTEGER
2 0 REAL
3 0 DOUBLE
4 FALSE BOOLEAN
5 NULL CHARACHTER
6 VOID REFERENCE REFERANCE TYPE

מערכים - ARRAY

הצהרה: 

keywords : ARRAY[STRING]
numbers: ARRAY[INTEGER]

יצירה(מערך מ1 עד 100):

create keywords.make(1,100)

רשומה

נקרא tuples מקבילה של struct או class ללא פונקציות רק data members:

t: TUPLE [name: STRING; weight: REAL; date: DATE]

יכול להוות תאריך או רשומה מסוימת וא"צ להגדיר מחלקה

["Brigitte", 3.5, Last_night]

גישה:

t.weight := t.weight + 0.5

אופרטורים

שם סימן
1 גדול <
2 קטן >
3 שווה =
4 שונה =/
5 גדול שווה =<
6 קטן שווה =>
7 השמה =:

IF

if תנאי
then גוף
else אם לא מתקיים התנאי
end סוף תנאי
if(num>10)  then

  output.putint (num)

  output.new_line

else

  output.putstring ("Error")

end

LOOP

from תנאי התחלה
until תנאי עצירה
loop/end גוף הלולאה
from I:=0  until I>10 loop

  RESULT := RESULT + 1

end

פונקציות ופרוצדורות

ישנה הסכמה בשפה לגבי הפרדה גמורה בין פונקציות לבין פרוצדורות.

פונקציות - עושות חישובים ומחזירות ערך, אבל לא יכולות לבצע שינויים במשתנים.
פרוצדורות - עושות שינויים במשתנים, אבל לא מחזירות ערך.

בצורה כזאת, אפשר לשלוח פונקציה בלי לדאוג שישתנו דברים בתוכנית. אך בפועל בשפה הקומפיילר לא אוכף נושא זה. שם הפונקציה בסוגרים פרמטרים ואחרי ערך מוחזר (אפשר להשמיט ערך מוחזר או פרמטרים או שניהם) הIS מכיל הערה שמתארת את הפונקציה.

CLASS

תוכנית אייפל ידועה כמערכת, ומרכיביה הם מחלקות. יכול להיות מספר כלשהו של מחלקות במערכת. רבות מהן מחלקות כלליות. וחלקן מחלקות השייכות למערכת. המערכת פיתוח אייפל מגיעה עם ספריות עם מחלקות מובנות בעלות יעוד. מחלקות בסיסיות רבות מוגדרות בשפה,והן חייבות להופיע.

  • note - הערות המחלקה
  • class - שם המחלקה
  • inherit - ממי יורש (אופציונלי)
  • create - פונקציות מאתחלות
  • local - משתני מחלקה
  • do-end - גוף המחלקה


note
description : "targil5 application root class"
date : "$Date$"
revision : "$Revision$"

class
  MY_CLASS

  inherit
      OTHER_CLASS

  create 
      Function1

  feature {NONE} my_function (variable_name: SOME_TYPE)-- Initialization
               -- Run application.
    local
       cmp : CLASS1
    do
      cmp.make
      ....
      ....
   end

end

 

מנגנון ניהול חריגות

לשפה יש מנגנון מאוד יעיל של ניהול חריגות שעובד בצורה כזאת שהוא משתמש בבדיקת ה-post condition שלו, ובמקרה שזה לא עובר, הוא עושה את הפונקציה מחדש בשביל לנסות שוב לפי מספר הניסיונות שהוגדרו לו מראש.
לדוגמה:

 connect_to_server (server: SOCKET) -- Connect to a server or give up after 10 attempts. 
 require 
    None_Null: server /= Void and then server.address /= Void 
 local 
   attempts: INTEGER 
 do 
   server.connect 
 ensure 
   connected: server.is_connected 
 rescue 
   if attempts < 10 then 
      attempts := attempts + 1 
 retry 
   end 
 end

אפשרויות שונות בשפה

קריאות חד פעמיות - משמש לאיפוס ערכים באובייקטים ועובד בצורה קלה ונוחה.

מנגנון ניהול חריגות - לשפה יש מנגנון מאוד יעיל של ניהול חריגות שעובד בצורה כזאת שהוא משתמש בבדיקת הpost condition שלו, ובמקרה שזה לא עובר, הוא עושה את הפונקציה מחדש בשביל לנסות שוב לפי מספר הניסיונות שהוגדרו לו מראש.

השפה משלבת שתי גישות לתרגום – יש לה גם מפרש, וגם מהדר. קוד שלא נעשה בו שינוי מאז הפעם האחרונה, כולל ספריות מערכת, הוא קוד שעבר הידור – ולכן הוא "קפוא". קוד ששונה "הותך", ועל ההרצה שלו מופקד המפרש של השפה.

היתרון של הטכנולוגיה, הוא שהיא נותנת לנו להנות מכל העולמות - מהירות הריצה שמספק קוד שכבר תורגם במלואו לשפת מכונה על ידי המהדר, ומהירות תגובה לשינויים בעזרת קוד מפורש.

תרגום לשפת מכונה

באייפל יש שני שלבי תרגום: החלק הראשון הוא הידור לקוד ביניים בעזרת מהדר, עליו רץ מפרש. אחר כך, הקוד מתורגם לשפת C, ובעזרת מהדר C הוא מהודר לשפת מכונה.

ע"פ מתכנני השפה, מטרת התרגום לשפת C כשפת ביניים, הוא ניצול שיטות הייעול הקיימות במהדרי שפת C. (לטענת מעצבי השפה, קוד בשפת Eiffel יכול להיות יעיל כאילו נכתב בשפת C עצמה או Fortran)

חווית המשתמש

התוכנה מתאימה לכותבים שצריכים לתאם ביניהם ממשקים, או שהשימוש בקוד לא ידוע מראש (ירושה מבוקרת). ה- writeabilty (הכתיבות) סביר, דרוש מעט זמן להתרגל לשפה, וכן יש הרבה דוקומנטציה על השפה היתרון הוא שהוא מפורט והחיסרון שהוא כמעט היחיד (עיין למטה בקישורים)

הערות כלליות

  • השפה הינה case insensitive - הקומפיילר אינו מבחין בין אותיות גדולות וקטנות, אבל מקובל להשתמש בסגנון כתיבה קבוע לשפה, כדי לעשות את הקוד קריא לכל מי שמכיר את השפה. ולכן נוהגים לכתוב: מחלקות - באותיות גדולות, פונקציות - באותיות קטנות, משתנים - אות ראשונה גדולה והשאר קטנות, מספר מילים - כותבים עם קו תחתון בין המילים.
  • באייפל אין חובה להבדיל בין פקודות באמצעות נקודה פסיק (;) בסוף שורה.
  • יש להבדיל בין אופרטור שוה (=) שמחזיר ערך בוליאני ואינו מבצע השמה לבין אפרטור השמה (=:)
  • הסימן -- (מינוס כפול) מבטא הערה.
  • פונקציות יצירה הם סוג של פונקציות בונות (אלא שקוראים להן אקטיבית) המשמשות לאיתחול והגדרות המחלקה יש להגדיר פונקציות כאלה בחלק CREATE במחלקה ולממשן בחלק FEATURS כמו כל שאר הפונקציות.
  • חלק IS בהגדרת הפונקציה מכיל הערה הממצה את מהות ה מחלקה ("מטפורה").
  • פונקציות נקראות בדומה למשתנים ולכן המשתמש לא צריך לדעת את אופן המימוש.
  • השפה מאד מבנית, משתמשת בבלוקים לכל קבוצת קוד. בדומה לADA.

בעיות ופתרונן

  • כאשר פונקציה מחזירה פרמטר יש להשתמש בפרמטר המובנה בשפה RESULT לצורך החזרה.
  • אין נגישות למערך אחרי הגדרתו. יש לעשות CREATE לאובייקטים כגון מערכים, קבצים וכו'.
  • בטיפוס מסוג אובייקט בצורה דפולטיבית השמה מחזיר פוינטר לאובייקט ולכן אם הוא נמחק קיים פוינטר מתנדנד ולכן רצוי להשתמש ב בCLONE או COPY
  • קיים קושי במציאת מדריך שפשוט מראה פקודות ולא רק מדבר על תאוריה של השפה.
  • פתיחת קבצים נעשית בצורה שונה מתוכניות אחרות, צריך להכניס את מיקום הקובץ למשתנה ורק אחרי זה להתחיל לפעול עליו.
  • מערכת שונה של החזרת ערך מהפונקציה במקום לעשות return מכניסים את הערך שרוצים אל המשתנה result. (בדומה לPL/SQL)

פלטפורמות

אייפל רצה על הפלטפורמות הבאות: Windows ,Linux, Unix ,VMS.

מהדרים