שגרה (תכנות)

בתכנות מחשבים, שגרה (באנגלית: Routine), תת-שגרה (באנגלית: Subroutine), פרוצדורה (באנגלית: Procedure), ובתכנות מונחה-עצמים שיטה/מתודה (באנגלית Method), היא רצף של פקודות המאוגדות יחדיו, במטרה לבצע מטלה מוגדרת, מימוש של אלגוריתם. פונקציה היא שגרה המחזירה ערך. שגרות מהן לא מוחזר ערך משמשות על פי רוב לשם שינוי מצב כלשהו – הדפסות למסך, שינוי של אובייקט, או תוצאות לוואי אחרות. קריאה לשגרה היא הפעלה שלה תוך כדי פעולת התוכנית. קריאה של שגרה לעצמה, ישירות או בעקיפין, נקראת רקורסיה. החלק בשגרה שמתבצע בפועל נקרא "גוף" השגרה. שגרה יכולה שלא לקבל קלט, לקבל קלט יחיד או מספר של קלטים כלשהם. שגרות הן עיקרה של פרדיגמת התכנות הפרוצדורלי. מקובל כי שימוש מושכל בשגרות ובפונקציות עשוי לשפר את מבנה התוכנית, את קריאות הקוד ואת מידת הגמישות של התוכנית לביצוע שינויים. שימוש בשגרות מאפשר חלוקה של קוד לחלקים קצרים – שגרות קצרות – וכך מקל על וידוא נכונות של כל אחד ממרכיבי הקוד בנפרד. תוצאה זו מאפשרת להפחית במידה משמעותית את עלויות הפיתוח והתחזוקה של תוכנה. בתכנות מונחה-עצמים, לכל עצם יש מספר שגרות השייכות אליו, שגרה כזאת נקראת "שיטה" (או "מתודה" בעקבות המונח באנגלית) או "פונקציה חברה" (++C), והיא מגדירה את ההתנהגות של האובייקט עליו היא נקראה, ופועלות על המידע הכמוס בתוכו או בעזרתו. בהתאם למידת החשיפה שלהן לשאר הקוד, שגרות אלה מהוות גם ממשק בין העצם לתוכנית כולה. במרבית שפות התכנות קיימות שגרות, פונקציות או מתודות. סט הפקודות של מעבדים מסוימים מכיל פקודות לתמיכה בשגרות. כמעט כל המעבדים המודרניים הם כאלו. במעבדים אלו, שגרות נתמכות גם בשפות סף ושפות מכונה. ישנן מספר דרכים להעביר פרמטרים לשגרות. הדרך הסטנדרטית להעברת קלט אל שגרות הוא באמצעות רשימת פרמטרים (ארגומנטים) – בקריאה לשגרה מצרפים גם סדרה של משתנים או קבועים. בצורה זו מתקבל תחביר דומה מאוד לכתיבה של פונקציה מתמטית: (Make(a,b, למשל. העברת המשתנים עשויה להיות על ידי העברה של הערך שלהם (By Value), כלומר העתקת תוכנם אל משתנים חדשים, או על ידי התייחסות (By Reference), המהווה בפועל נתינת שם נוסף לאותו משתנה. בהעברת משתנים לפי ערך, המשתנים המקוריים שנשלחים למתודה אינם משתנים בפועל. כלומר, למתודה נשלחים העתקים של הערכים שלהם, ולא המשתנים עצמם בפועל. בהעברה על ידי התייחסות נשלחים למתודה הכתובות של המשתנים ולא העתקים של הערכים שלהם. במתודה כזו למשל שמקבלת שני משתנים ומחליפה ביניהם – השינוי יהיה ניכר גם לאחר סיום המתודה. בהעברה משתנים לפי ערך למתודה כזאת, תתבצע רק החלפה שלהם "באוויר". המשתנים עצמם לא ישתנו בפועל, כאילו שהמתודה לא פעלה מעולם. במקרה שמעבירים על ידי ערך (בשפת C, למשל, זו הדרך היחידה) ויש צורך לשנות את המשתנה המקורי, ניתן להשתמש בחלק מהשפות במצביע. המצביע מכיל את הערך המספרי של כתובת המשתנה בזיכרון המחשב, וכך ניתן לגשת ישירות לכתובת זו ולשנות את ערכו של המשתנה. ישנן שפות המאפשרות גם העמסה (function overloading), כלומר, מתן שם זהה למספר שגרות המשמשות למטרה דומה, אך עם קלט שונה (או חתימה שונה, לפי הגדרות השפה). מקרה פרטי של העמסה הוא אפשרות למתן ערכי ברירת מחדל לפרמטרים. שפות אחדות, בהן #C ופייתון, מאפשרות גם העברת פרמטרים לפי שמם בהצהרת השגרה. לשגרה עשויות להיות תוצאות לוואי, המשפיעות על מצב התוכנית כולה, למשל על ידי שינוי של מבנה נתונים בזיכרון המחשב, או פעולה על הפרמטרים במקרה של העברת פרמטרים לפי התייחסות, או העברה של מצביעים. לשגרות או פונקציות המבצעות פלט וקלט מול המשתמש או זיכרון המחשב יש לרוב תוצאות לוואי. פונקציות בשפות תכנות, שואבות את שמן מפונקציות מתמטיות, ואכן מימוש של פונקציה מתמטית בשפת תכנות יעשה בדרך כלל באמצעות מבנה ההפשטה של פונקציות. אולם, לעיתים קרובות, פונקציות בשפות תכנות אינן ניתנות לתיאור כפונקציות מתמטיות פשוטות. בתכנות פרוצדורלי, כגון בשפת C, אין הבדל מהותי בין פונקציה לשגרה, ולא תמיד יש הבחנה בתחביר בין שגרות לפונקציות – לעיתים ההבדל היחיד הוא השימוש בערך ההחזרה מפונקציה על מנת לדווח על מידת הצלחת הפעולה שהתבצעה. בשפת פסקל קיים הבדל תחבירי בין פונקציה המחזירה ערך לבין פרוצדורה שאינה מחזירה ערך. עם זאת, באופן מהותי, קריאה לפונקציה מהווה ביטוי, כלומר התייחסות לערך כלשהו, לעומת קריאה לפרוצדורה המהווה "פקודה", כלומר שינוי מצב של משתנים בדרך כלשהי. בתכנות פונקציונלי, פונקציה היא טיפוס נתונים בסיסי לכל דבר, והוא לב ליבה של השפה. בשפות פונקציונלית (בהן LISP, Haskell, או ML) לפונקציות, ולביטויים בכלל, אין תוצאות לוואי, בשאיפה. דבר זה מקל על הוכחת נכונות של קטע קוד, ולעיתים גם על עיבוד מקבילי. מתודה לא-וירטואלית – מתודה שקריאה אליה מתפרשת על פי הטיפוס הסטטי של האובייקט. מתודה וירטואלית – מתודה שמוגדרת באובייקט אב כמתודה וירטואלית. אובייקטים שיירשו ממחלקת האב יוכלו "לדרוס" (Override) את המתודה ממחלקת האב באופן אחר, וכך מתאפשר פולימורפיזם של זמן ריצה: התנהגות שונה של אובייקטים בעלי טיפוס סטטי זהה, על פי הטיפוס הדינמי שלהם. מתודה אבסטרקטית (או מתודה וירטואלית-טהורה) – מתודה שמוגרת במחלקת אב אבסטרקטית (שלא ניתן ליצור לה מופע), והמחלקות היורשות צריכות לממש אותה. להבדיל ממתודה וירטואלית, מתודה אבסטרקטית באובייקט האב איננה חייבת להכיל קוד, ובשפות מסוימות אף איננה עשויה להכיל קוד. מתודה סטטית – מתודה הניתנת לגישה באופן סטטי, כלומר שאין צורך במופע של מחלקה על מנת לגשת אליה. מתודה סטטית היא למעשה פונקציה, הנמצאת במרחב השם של המחלקה, ובשפות מסוימות היא בעלת הרשאות דומות להרשאות של מתודות שאינן סטטיות (מבחינת גישה למשתנים פרטיים וכדומה). העמסת פונקציות או מתודות – הגדרה של מספר פונקציות תחת אותו שם אבל עם חתימה שונה (מספר או טיפוס שונה של פרמטרים). דוגמה להעמסת פונקציות היא הגדרה של שתי פונקציות בשם max: אחת שתגדיר מקסימום בין משתנים מטיפוס int, והשנייה שתגדיר מקסימום בין משתנים מטיפוס double. העמסה היא מקרה פרטי של פולימורפיזם מוגבל של זמן קומפילציה. על פי רוב, תחום הכרזה (scope) של משתנים המוגדרים בתוך פונקציה הוא הבלוק שבו הוגדרו, או הפונקציה עצמה. משך החיים של משתנים כאלה מוכל לרוב בתוך משך החיים של הפונקציה; עם זאת, ישנן שפות (כגון שפת C) המאפשרות להגדיר משתנים "סטטיים" – כאלה שמשך החיים שלהם הוא כל אורך חיי התוכנית. משתנים כאלה מובחנים ממשתנים גלובליים רק בטווח ההכרה שלהם. דוגמה לפרוצדורה בשפת C: void print_num(int num) { printf("%d", num); } פרוצדורה זו קוראת לפונקציה קיימת בשם printf על מנת להדפיס מספר כלשהו שהועבר אליה, למשל המספר 5, כך: print_num(5); הפרוצדורה איננה מחזירה ערך, והיא נקראת אך ורק לשם תוצאת הלוואי שלה - הדפסה למסך. קטע הקוד הבא בשפת C מגדיר פונקציית סכום בין שני מספרים שלמים: int sum(int a, int b) { return a+b; } השימוש בפונקציה הוא לשם קבלת התוצאה שלה. לדוגמה אם נרצה להדפיס את סכום המספרים 1 ו-2, נוכל להשתמש בה: print_num(sum(1,2)); //prints 3 בתכנות מונחה-עצמים, מתודות מוגדרות כחלק ממחלקה. בדוגמה הבאה (בשפת ++C) נעשה שימוש בדוגמאות לשגרה ולפונקציה, אך בנוסף ישנן מתודות שנעזרות במידע הנשמר בתוך האובייקט: struct SumPrinter { int a, b; void set(int x, int y) { a=x; b=y; } void printSum() { print_num(sum(a, b)); } }; קוד שישתמש במחלקה הזאת עשוי להיראות כך: SumPrinter mp; mp.set(1, 2); // inside mp, a=1 and b=2 mp.printSum(); //prints 3 מונחים בתוכנה טבלת מתודות וירטואלית מחסנית קריאות ראו Method בוויקיפדיה באנגלית.