Presentation is loading. Please wait.

Presentation is loading. Please wait.

מבוא כללי למדעי המחשב שיעור 5

Similar presentations


Presentation on theme: "מבוא כללי למדעי המחשב שיעור 5"— Presentation transcript:

1 מבוא כללי למדעי המחשב שיעור 5
מרצה: שי גוטנר

2 חזרה - פונקציות הגדרה של פונקציה :
return_value_type function_name(parameter list) } declarations statements { גוף הפונקציה (מימוש)

3 חזרה - פונקציות return_value_type function name
float Maximum (float x, float y) { if (x > y) return x; else return y; } parameter list בפונ' ניתן להשתמש בפרמטרים שהעוברו ובמשתנים המוגדרים בה.

4 קבועים אם רוצים להגדיר ערכים קבועים שיהיו בשימוש כל חלקי התוכנית, אין צורך במשתנה גלובלי, וניתן להגדיר קבוע על-ידי: #define PI (מעדיפים לראות בתוכנית שמות משמעותיים של קבועים מאשר מספרים שמקורם לא ברור)

5 הפיכת תוכנית עם main בלבד לתוכנית עם פונקציות נוספות
int main() { int i, n, fact=1; scanf(“%d”,&n); for(i = 1; i <= n; i++) fact *= i; printf(“%d\n”,fact); return 0; } מה עושה התוכנית? איזה חלק נהפוך לפונ' ?

6 הפיכת תוכנית עם main בלבד לתוכנית עם פונקציות נוספות
int fact(int); int main() { int n; scanf(“%d”,&n); printf(“%d! = %d”, n, fact(n)); return 0; } Prototype Function Call

7 הפיכת תוכנית עם main בלבד לתוכנית עם פונקציות נוספות
Function Definition int fact(int n) { int i, res = 1; for (i = 1; i <= n; i ++) res *= i; return res; }

8 שליטה בתוכנית כאשר קוראים לפונקציה השליטה בתוכנית עוברת אליה
פקודת return מחזירה את השליטה לפונקציה הקוראת (אם אין פקודת return , בסוף הפונ' מוחזרת השליטה) אם הפונ' מחזירה ערך עלינו לאחסנו/להשתמש בו מייד בחזרה מהפונקציה

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

10 rand() בשפת C ניתן לייצר מספרים פסאודו-אקראיים ע"י הפונקציה
int rand(void); X = rand(); הפונקציה נמצאת ב-stdlib.h הפונקציה מחזירה מספר בטווח 0-RAND_MAX אם משתמשים ב-rand מוטב לבצע קריאה אחת ל-srand בתחילת התוכנית void srand(unsigned int);

11 שימוש ב- srand השימוש יהיה כך srand(time(NULL));
time פונקציה שמחזירה את הזמן ונמצאת בספרייה time.h

12 rand איך נייצר ערכים אקראיים בטווח אחר מאשר RAND_MAX?

13 rand איך נייצר ערכים אקראיים בטווח אחר מאשר 0-RAND_MAX? נשתמש בשארית
rand() % 10 /*randomly in 0 – 9*/ rand() % /* randomly in 1 – 10*/

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

15 פונקציות – תוכנית המחשבת סכום המספרים עד n
#include <stdio.h> int compute_sum(int n); int main() { int n=3,sum; printf(“%d\n”,n); sum = compute_sum(n); printf(“%d\n”,sum); return 0; }

16 פונקציות – תוכנית המחשבת סכום המספרים עד n
int compute_sum(int n) { int sum = 0; for( ; n > 0 ; --n) sum + = n; return sum; } איזה משתנים מוגדרים והיכן?

17 פונקציות – תוכנית המחשבת סכום המספרים עד n
main Comupte_sum n sum 3 2 1 5 6 n sum 3 6

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

19 רקורסיות compute_sum(n) = compute_sum(n-1) + n compute_sum(1) = 1
הגדרה זו היא הגדרה רקורסיבית של הפונקציה. חישוב הסכום של n איברים מסתמך על תוצאת הסכום של n-1 האיברים הקודמים. חובה ! תנאי התחלה.

20 נוסחת הרקורסיה תנאי קצה
compute_sum(n) = compute_sum(n-1) + n compute_sum(1) = 1 תנאי קצה 1 n n-1 n-2 n-3 מה סכום המספרים

21 מימוש רקורסיה #include <stdio.h> int compute_sum(int n);
int main() { int n=3,sum; printf(“%d\n”,n); sum = compute_sum(n); printf(“%d\n”,sum); return 0; }

22 מימוש רקורסיה תנאי קצה int compute_sum(int n) { if ( n == 1 )
return 1; return n + compute_sum(n-1); } נוסחת הרקורסיה

23 רקורסיות - ריצה נניח שב-main יש לנו את הקריאה הבאה: compute_sum(5);
הערך המוחזר ערכי הקריאה מקום הקריאה 5 + sum(4) Sum(5) main() 4+sum(3) Sum(4) 3+sum(2) Sum(3) 2 + sum(1) Sum(2) 1 Sum(1)

24 עצרת נסתכל על ההגדרה של עצרת כפי שראינו בעבר n*(n-1) = 1*2*3* …n!
מהי ההגדרה הרקורסיבית של העצרת ?

25 עצרת נסתכל על ההגדרה של עצרת כפי שראינו בעבר n*(n-1) = 1*2*3* …n!
מהי ההגדרה הרקורסיבית של העצרת ? n! = (n-1)! * n 1! = 1 , 0! = 1

26 int fact(int n) { if (n<=1) return 1; return n * fact(n-1); } int fact_i(int n) { int prod=1; while (n>1) prod *= n; n--; } return prod;

27 רקורסיות - ריצה נניח שב-main יש לנו את הקריאה הבאה: fact(4);
הערך המוחזר ערכי הקריאה מקום הקריאה 4*fact(3) fact(4) main() 3*fact(2) fact(3) 2*fact(1) fact(2) 1 fact(1)

28 רקורסיות - ריצה נניח שב-main יש לנו את הקריאה הבאה: fact_i(4);
הערך המוחזר prod n 1 4 3 4*3 2 24 4*3*2

29 חישוב סכום הספרות בדומה לשיעורי הבית
נרצה לחשב את סכום הספרות של מספר רעיון כללי:

30 חישוב סכום הספרות של מספר
נרצה לחשב את סכום הספרות של מספר רעיון כללי: לולאה כאשר בכל פעם מחלצים את ספרת האחדות: sum = 0 while ( n != 0 ) sum = sum + ספרת אחדות n = n / 10

31 חישוב סכום הספרות int DigitSum(int n) { int sum = 0; while(n >0)
sum += n %10; n /= 10; } return sum;

32 חישוב סכום הספרות מעבר לרקורסיה תנאי קצה נוסחת הנסיגה

33 חישוב סכום הספרות int DigitSumRec(int n) } if (n > 0)
return n % DigitSumRec(n / 10); else return 0; {

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

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

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

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

38 קריאה לפונקציה 5 y 2 משתנים מקומיים Foo(5, y, 2);

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

40 דוגמא מסכמת נתונה הנוסחה הרקורסיבית הבאה עבור סדרת פיבונאצ'י:
Fib(0) = 0, Fib(1) = 1; Fin(n) = Fib(n -1) + Fib(n-2) הערכים של סדרה זאת הם: 0,1,1,2,3,5,…… כיצד נתרגם את הנוסחא הזו לתוכנית רקורסיבית ? במה שונה נוסחא זו ממה שראינו עד עכשיו?

41 דוגמא מסכמת int Fib(int n) { if (n<= 1) return n; else
return Fib(n-1) + Fib(n -2); }

42 דוגמא מסכמת n ערך מספר קריאות 1 2 3 23 28657 92735 24 46368 150049

43 החלפה בין משתנים החלפת הערכים החלפת הערכים נסתכל על התוכנית הבאה:
int main() { int a,b,x,y,temp; ... temp = x; x = y; y = temp; temp = a; a = b; b = temp; } החלפת הערכים החלפת הערכים

44 החלפה בין משתנים כפי שלמדנו במצב כזה הינו רוצים שתהיה פונקציה שתבצע את החלפה הזו בין המשתנים.

45 החלפה בין משתנים נכתוב פונקציה כזו בשם swap void swap(int p, int q) {
int temp; temp = p; p = q; q = temp; }

46 החלפה בין משתנים int main() { int a=10,b=2,x=3,y=5; ... swap(x,y);
swap(a,b); }

47 החלפה בין משתנים נעקוב אחרי הקריאה לפונקציה swap(x,y); 3 x 6 y
void swap (int p,int q) { int temp; temp = p; p = q; q = temp; } 6 p 3 q

48 מצביעים ראינו שכל משתנה שהגדרנו בתוכנית שלנו מוצקה עבורו תא באזור כלשהו בזיכרון. int main() { int x,y; } לכל תא כזה יש כתובת בזיכרון של המחשב. כדי להגיע לכתובת של משתנה משתמשים באופרטור &. x y

49 מצביעים מה יחזירו &x ו &y . x 5000 y 5004

50 מצביעים ניתן להגדיר משתנים שסוגם הוא מצביע.
משתנים כאלה מחזיקים בתוכם את הכתובות. int main() { int x,y; int *p; p = &x; } 5000 x 5000 y 5004 p 9008

51 מצביעים כדי לסמן שמצביע לא מצביע לשום מקום נהוג להשים לו NULL
p = NULL; אפשר לחשוב על משתנה מסוג מצביע כאל חץ: x y p

52 מצביעים int main() { int x=7,*p = &x;
printf(“Value %d Pointer %p\n”, x, p); } כדי להדפיס את הערך שאליו מצביע p. printf(“Value %d\n”,*p) פורמט הדפסה של משתנה מסוג מצביע

53 מצביעים יש לשים לב שאין הצבעה לביטויים ולקבועים &3 &(k+99)

54 Call by value and call by reference
נחזור לפונקצית ה swap. הבעיה הייתה שהחלפת הערכים התבצעה בין הפרמטרים של הפונקציה ולא בין המשתנים עצמם.

55 Call by reference int main() { int a=10,b=2,x=3,y=5; ... swap(&x,&y);
swap(&a,&b); } void swap (int* p,int* q) int temp; temp = *p; *p = *q; *q = temp;

56 Call by reference בכדי לממש call by reference יש לבצע את הדברים הבאים:
1 להגדיר את הפרמטרים של הפונקציה כמצביעים void swap(int *p, int *q) 2 להשתמש בגוף הפונקציה בערכים שאליהם הם מצביעים *p = *q; 3 בקריאה לפונקציה להעביר כתובות כארגומנטים swap(&x, &y);

57 חזרה - מערכים נגדיר בעזרתו קבוצת משתנים כאשר יהיה לנו מספר רב של משתנים זהים char a[10]; int b[50]; הגודל חייב להיות קבוע. פנייה לאיברי המערך מתבצעת ע"י a[k] כאשר k בין 0 ל- )-1גודל המערך) אתחול ע"י לולאה או רשימת אתחול

58 מצביעים מצביע הוא משתנה שמכיל כתובות int *p, *q; char *p;
פנייה לכתובות p = &x; p = q; פנייה לערך ש-p מצביע אליו: *p = 5; x = *p;

59 הקשר בין מערכים ומצביעים
כאשר אנחנו מגדירים מערך בגודל 20 של שלמים אזי יש לנו 20 משתנים מסוג int בזיכרון. מה הכתובת של משתנים אלו ? int a[20]; a[0] כמו לכל משתנה, גם לאיברי המערך ישנה כתובת. כיצד נגיע לכתובת זו? a[19]

60 הקשר בין מערכים ומצביעים
ניתן להגיע לכתובות בדרך הרגילה int *p; p = &a[0]; נזכור שהמערך ממוקם בצורה רציפה בזיכרון לכן כתובות איברי המערך הן רציפות, למשל: 5000 5004 5008

61 משמעות שם המערך פניה לשם המערך נותנת לנו את כתובת המערך שהיא למעשה מצביע לאיבר הראשון במערך. בשם המערך ניתן להשתמש כדי לקבל את הכתובת הזו. לא ניתן לשנות אותו (כלומר הוא מצביע קבוע) int *p; p = a; p מצביע לראש המערך. (את p כמובן שניתן לשנות)

62 חשבון של מצביעים ניתן להשתמש בפעולות חיבור וחיסור על כתובות. זה שימושי בעיקר בשילוב עם מערכים. *(a+i) שקול a[i]; *(p+i) שקול p[i];

63 חשבון של מצביעים מספר דרכים לכתוב לולאה שרצה על מערך:
for (i=0; i<n; i++) sum + = a[i]; for (p = a, i=0; i<n; i++) sum + = *(p+i); for (p=a; p<=&a[n-1]; p++) sum + = *p;

64 חשבון של מצביעים כיצד ה++ יודע לקדם את המצביע לאיבר הבא ?
הבעיה: המערך יכול להיות של תווים, של שלמים או של כל דבר. המרחק בין התאים הוא שונה! מה המרחק עבור int ? מה המרחק עבור char ?

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

66 העברת מערך לפונקציה כיצד נכתוב פונקציה שמקבל כערך מערך ומבצעת חישוב על איבריו? נניח שנרצה לכתוב פונקציה שמקבלת מערך של מספרים ומחזירה את סכום איברי המערך. העברת מערך לפונקציה מתבצעת למעשה ע"י העברת מצביע לראש המערך. int sum(int a[ ], int size); prototype

67 העברת מערך לפונקציה a[ ] זהו פשוט מצביע למערך של int.
הפרמטר השני הוא גודל המערך. מאחר ואנחנו עובדים עם מצביעים, אין לנו מידע מהו גודל המערך ולכן נעביר את המידע הזה כפרמטר לפונקציה.

68 העברת מערך לפונקציה ההגדרות הבאות שקולות: int f(float arr[ ]);

69 העברת מערך לפונקציה int sum(int a[ ], int size) { int i, res = 0;
for(i = 0; i < size; i++) res += a[i]; return res; }

70 העברת מערך לפונקציה int sum(int *p, int size) { int i, res = 0;
for( i = 0; i < size; i++ ) res += p[i]; return res; }

71 מחרוזות מחרוזות ב C הן פשוט מערכים שאיבריהם הם תווים.
המוסכמה בנוגע למחרוזות היא שמחרוזות מסתימות ב תו מיוחד אשר מסמן את סוף המחרוזת. תו זה הוא ‘\0’ שקוד ה ascii שלו הוא 0.

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

73 מחרוזות מה עושה הפונקציה הזו?
int strlen(char *s) { int cnt=0; while (*s != ’\0’) cnt++; s++; } return cnt; מה עושה הפונקציה הזו? איפה משתמשים בעובדה שמחרוזת מסתיימת בתו מסוים?

74 מחרוזות int strlen(char *s) { char *ptr=s; while (*ptr++ != ’\0’) ;
return ptr-s-1; } מה בעצם קורה בתנאי קידום הלולאה ומה הדרך הנכונה לקרוא את זה?

75 מחרוזות int strdo(char *s,char ch) { int cnt=0; while (*s != ’\0’)
if (*s == ch) cnt++; s++; } return cnt; מה עושה הפונקציה הזו?


Download ppt "מבוא כללי למדעי המחשב שיעור 5"

Similar presentations


Ads by Google