הפקולטה למדעי המחשב פרופ' חיים גוטסמן, מר רן רובינשטיין עומר סטרולוביץ, כרמי גרושקו, אלכסנדר ליבוב, מיכאל בלטקסה, ראידה נעאמנה, שי מרקנטי, גיא שקד הטכניון - מכון טכנולוגי לישראל 3.10.2011 מבוא לתכנות מערכות 234122 מבחן מועד ב' סמסטר אביב 2011 שאינן קשורות הוראות כלליות משך המבחן: 180 דקות (שלוש שעות). טופס המבחן מכיל 18 עמודים, כולל עמוד זה. יש לענות על כל השאלות במקום המיועד לכך בטופס. מותר לכתוב גם בעט וגם בעיפרון. מומלץ ביותר ראשית לקרוא כל שאלה עד סופה, ורק אח"כ לענות. מותר השימוש בכל חומר עזר כתוב או מודפס. יש להקפיד על כתיבה ברורה ומסודרת של התשובות. אם הנכם מוצאים צורך להניח הנחות כלשהן, ציינו אותן במפורש ונמקו. על התשובות להיות קצרות ומדויקות ככל הניתן. אין לרשום קוד מיותר או עובדות כלליות לפתרון. אין צורך לתעד את הקוד בפתרונות. אין צורך להקפיד על code conventions כלשהם בעת כתיבת קוד. מבנה המבחן: שאלה 1 2 3 4 5 6 7 סה"כ ניקוד /18 /12 /8 /12 /20 /15 /15 /100 בהצלחה! 1
(ADT) שאלה 1: (18 נק') בשאלה זו נעסוק בתכנית C לניהול חנות בגדים. התוכנית מחולקת הכוללים: חנות,(shop) לקוח (customer) ובגד.(clothing) למספר טיפוסי נתונים מופשטים על טיפוס הנתונים עבור לקוח להכיל את שם הלקוח, וכן מבנה נתונים שמכיל את הבגדים אותם הוא מתכוון לרכוש. שימו לב: על מבנה הנתונים הזה לאפשר ייצוג של הבגדים על פי הסדר בו הלקוח בחר בהם, לצורכי סטטיסטיקה. א. השלימו את שמות הקבצים והקוד הדרוש להגדרת ה- ADT עבור לקוח. ניתן להשתמש בכל אחד ממבני הנתונים שנלמדו בקורס,Set) Stack,List ו- Graph ). יש לרשום את הגדרות הטיפוסים הדרושות. - יש להשלים את כל הוראות ה- preprocessor ולרשום את הוראות ה- include הדרושות להגדרת הטיפוסים. - אין צורך להכריז על או לממש פונקציות מנשק בסעיף זה. - אין צורך להגדיר טיפוס להחזרת ערכי שגיאה. - שם הקובץ: שם הקובץ: #ifndef #define #endif 2
struct shop_t { Set customers;... } ב. נניח מעתה שבקובץ,shop.c קיים המבנה shop_t שמוגדר כך: // all customers in store כמו כן בקובץ,shop.h הטיפוס Shop מוגדר כמצביע למבנה זה. עתה, בהינתן חנות ושם לקוח מסוים בחנות, יש לממש את הפונקציה הבאה שמדפיסה בזה אחר זה את כל הבגדים שבחר הלקוח הנתון, על פי הסדר בו הם נבחרו (במידה והלקוח אינו קיים בחנות, לא יודפס דבר). מספר הערות : - הניחו שה- ADT עבור בגד מספק פונקצית הדפסה שחתימתה: void clothingprint(file* stream, Clothing c); במידת הצורך, הוסיפו פונקציות ב- ADT של לקוח המאפשרות את מימוש הפונקציה המבוקשת. כתבו את מלוא הקוד שיש להוסיף לקובץ ה- h ולקובץ ה- c במקרה זה. - void shopprintcustomerclothes(shop shop, FILE* stream, const char* name) { if (shop == NULL stream == NULL name == NULL) { return; } SET_FOREACH(Customer, c, shop->customers) { } } קוד נוסף: (ציינו באילו קבצים עליו להופיע) 3
4 - הטויט - - הטויט -
שאלה 2: (12 נק') ברצוננו לכתוב פונקציה גנרית בשם count_if בשפת C, אשר מקבלת מערך של עצמים כלשהם, פונקצית כלשהי, וכן פרמטר שלישי שמייצג ארגומנט לתנאי, ומחזירה את מספר העצמים במערך העומדים בתנאי. תנאי לדוגמה, עבור מערך של מספרים שלמים, תנאי אפשרי הינו "כל המספרים הגדולים מ- ", והארגומנט לתנאי יהיה 3. במקרה זה הפונקציה count_if תחזיר את מספר האיברים שערכם גדול מ- 3 במערך. א. כתבו את הפונקציה count_if בשפת C (כולל מימוש). הסבירו בקצרה כל פרמטר בחתימת הפונקציה. ב. באמצעות הפונקציה מסעיף א', ממשו פונקציה המקבלת מערך של מילים words[] char* באורך n ומחרוזת s, ומחזירה את מספר המילים במערך words שנמצאות אחרי s בסדר לקסיקוגרפי (אם s עצמה מופיעה אין לספור אותה). ממשו פונקציות עזר נוספות (שאינן חלק מהספריה הסטנדרטית) במידת הצורך כך שהקוד יתקמפל ויעבוד כנדרש. int numwordsafter(char* words[], int n, char* s) { 5
6 - הטויט - - הטויט -
7 שאלה 3: (8 נק') ברצוננו לכתוב מערכת מחשוב מסודרת אשר תשמש כבסיס נתונים עבור ספריות. בידינו קובץ יחיד אשר מכיל את כל הנתונים על המלאי של הספריות בפורמט הבא: לדוגמה: <library name>;<book name> Library of the Congress;Moby Dick Israel National Library;Zohar Technion Central Library;Electric Circuits Fundamentals Technion Central Library;Communication Systems כתבו תסריט C-Shell אשר מקבל שם של קובץ כנ"ל ושם תיקיה (directory) ויוצר תיקיה חדשה תחת התיקיה שהתקבלה עבור כל ספריה ובתוכה קובץ יחיד בשם books.txt אשר מכיל את רשימת שמות של כל הספרים בספריה ממוינים בסדר לקסיקוגרפי עולה. שם התסריט יהיה build_libraries ואופן ההפעלה יהיה: הערות: ניתן להניח כי אין תיקיות בתוך התיקייה הנוכחית בזמן הרצת התסריט. - ניתן להניח כי התו ";" אינו מופיע בשמות הספרים או שמות הספריות. - build_libraries <inputfile> <outputdir>
8 - הטויט - - הטויט -
9 שאלה 4: (12 נק') ברשותנו מאגר מידע על מורים פרטיים למתמטיקה וברצוננו למצוא מורה פרטי טוב. ברשותנו קובץ המכיל את רשימת המורים הפרטיים בשם teachers.txt המכיל שורות מהמבנה הבא: <id> <first_name> <last_name> <PhoneNumber> ניתן להניח שמבנה הקובץ תקין ושההפרדה בין העמודות היא ע"י רווח בודד, ושהשם לא מכיל רווחים ואין שורות ריקות באמצע. בנוסף, לכל מורה אשר לימד לפחות תלמיד אחד ומופיע ברשימה זו קיים קובץ בשם.<id>.grades כאשר <id> הוא מספר תעודת הזהות של המורה. קובץ זה יכיל את הציונים הסופיים של תלמידים אשר למדו אצל המורה, ומבנה השורה בו הוא כדלקמן: <student id> <grade> הנכם מתבקשים לספק לתלמיד תסריט בשם best_teacher אשר מקבל מספר ומחזיר את פרטי המורה הטוב ביותר אשר לימד לפחות סטודנטים שונים. המורה הטוב ביותר הוא כמובן המורה אשר לתלמידיו יש את הממוצע הגבוה ביותר. בנוסף, על התסריט להדפיס את ממוצע הציונים שקיבלו תלמידיו של המורה. לדוגמה: עבור קבצי קלט מתאימים פקודה זו תגרום להדפסת: bestteacher 10 259753159 Oded Gabi 052-5975315 94 הערות: אם קיימים מספר מורים בעלי ממוצע זהה ניתן להדפיס כל אחד מהם. - אם לא קיים אף מורה העומד בקריטריונים תודפס הודעת שגיאה מתאימה לבחירתכם. - ניתן לבצע את כל החישובים על מספרים שלמים ולהתעלם מהשארית במקרה של חלוקה. - אסור להשתמש בקבצי ביניים. - שימו לב שעבור מורים שלא לימדו תלמידים כלל לא קיים קובץ מתאים. -
שאלה 5: (20 נק') ברצוננו להגדיר מחלקה בשם ComplementSet ב-++ C, המייצגת קבוצה של מספרים שלמים. התכונה של קבוצה זו היא שהיא מכילה את כל המספרים השלמים, פרט למספר סופי של שלמים שאינם בקבוצה. במילים אחרות, המשלים של קבוצה זו (השלמים שאינם בקבוצה) הוא סופי. לדוגמה: קבוצת השלמים הגדולים מ- 20 בערך מוחלט היא.ComplementSet לעומת זאת, קבוצת המספרים השלמים הזוגיים איננה.ComplementSet הגדירו וממשו את המחלקה ComplementSet (כולל מימוש ויעבוד נכון: של כל הפונקציות), על מנת שקטע הקוד הבא יתקמפל #include "ComplementSet.h" #include <iostream> using namespace std; int main() { ComplementSet s1, s2; // two sets containing all integers for (int i=0; i<100 ; i++) { s1 -= 2*i; // remove integer from s1 (if already not there, do nothing) s2 -= 3*i; // remove integer from s2 (if already not there, do nothing) } for (int i=0; i<100 ; i++) { s1 += 3*i; // add integer to s1 (if already there, do nothing) s2 += 2*i; // add integer to s2 (if already there, do nothing) } ComplementSet s3 = s1 + s2; // union of sets ComplementSet s4 = s1 * s2; // intersection of sets; if (s3.contains(6)) cout << "s3 contains 6" << endl; set<int> s5 = -s1; complementset s6 = -s5; // get the finite complement of s1 // now s6 == s1 } return 0; הערות: - ניתן להשתמש במחלקה.std::set<int> - ניתן להשתמש בכללים הבאים (חוקי דה-מורגן) עבור קבוצות ( הוא המשלים של ( = = (למשל, החוק הראשון אומר שאיחוד של משלימים של שתי קבוצות זהה למשלים של החיתוך של שתי הקבוצות). - לשימושכם, נתונות הפונקציות הבאות המחזירות חיתוך ואיחוד של קבוצות של שלמים, בהתאמה: set<int> setintersect(const set<int>& a, const set<int>& b); set<int> setunion(const set<int>& a, const set<int>& b); 10
11
12 - הטויט- - הטויט -
13 שאלה 6: (15 נק') בתרגול 11 ראינו את אוסף המחלקות עבור צורות, להלן תזכורת של הקוד עבור המחלקות האלו: class Shape { int center_x, center_y; public: Shape(int x, int y) : center_x(x), center_y(y) {} virtual ~Shape() {} virtual double area() const = 0; }; class Circle : public Shape { int radius; public: Circle(int x, int y, int radius) : Shape(x, y), radius(radius) {} virtual double area() const { return radius * radius * PI; } }; class Square : public Shape { int sidelength; public: Square(int x, int y, int sidelength) : Shape(x, y), sidelength(sidelength) {} virtual double area() const { return sidelength * sidelength; } }; ברצוננו להוסיף את המתודה equals למחלקת,Shape אשר תאפשר השוואת שתי צורות. equals תופעל על צורה, תקבל כפרמטר צורה נוספת ותחזיר ערך בוליאני המציין אם שתי הצורות שוות (true) או לא.(false) שתי צורות ייקראו שוות אם הן מאותו סוג וערכי שדותיהם זהים. להלן מספר דוגמאות: 1. אם צורה א' היא עיגול וצורה ב' היא ריבוע - הצורות שונות 2. אם צורה א' היא עיגול שרדיוסו 3 וצורה ב' היא עיגול שרדיוסו 4 הצורות שונות 3. אם צורה א' היא עיגול שמרכזו ב-( 1,2 ) וצורה ב' היא עיגול שמרכזו ב-( 3,4 ) - הצורות שונות 4. אם שתי הצורות הינן עיגול שרדיוסו 2 ומרכזו ב-( 1,1 ) - הצורות שוות הוסיפו קוד למחלקות עבור המתודה.equals על הפתרון להתאים להוספת צורות נוספות בעתיד, דהיינו, תוספת של צורה חדשה לא תגרור שינוים בקוד של צורות קיימות. - רמז: ניתן (אך לא חובה) להשתמש ב- dynamic_cast.
14 - הטויט- - הטויט -
15 שאלה 7: (15 נק') להלן מנשק מחלקת התבנית Array אשר מטרתה לאפשר יצירת מערכים בטוחים של עצמים כלשהם: template <class T> class Array { int size; T* data; public: Array(int size); ~Array(); Array(const Array& a); Array& operator=(const Array& a); T& operator[](int index); const T& operator[](int index) const; }; ברצוננו להוסיף למחלקה תמיכה באתחול מאוספים קיימים שונים. למשל, נרצה שהפונקציה הבאה תאתחל את המערך בערכים שלמים הנלקחים מרשימה מקושרת קיימת: Array<int> init(std::list<int> old) { Array array(old.begin(), old.end()); return array; } הוסיפו תמיכה באתחול המערך בעזרת אוספים שונים כך שפתרונכם יתאים לדוגמה. ממשו את כל פונקציות העזר בהן אתם משתמשים. - ודאו שאין דליפות זיכרון במקרה של זריקת חריגה. -
16 - הטויט- - הטויט -
17
18