פרק ראשון
תכנית פייתון - רכיבי יסוד
תוכן העניינים
(4) ערכים, סוגים של ערכים, ושמות של סוגי ערכים
(6.1) מטרות, תחביר ועקרונות שימוש
(6.2) כללים ומוסכמות בקביעת שמות משתנים
(7.1) הדפסת ערכים ל-shell – הפונקציה print
(7.2) קליטת ערכים מה-shell – הפונקציה input
(7.3) המרות – הפונקציות float, int, ו-str
(7.4) בדיקת סוג: הפונקציה type
(9) תכנית ראשונה: עלות קניית ספרים
הוראות המופיעות בגוף של מבנה while ומתבצעות כל עוד תנאי הלולאה מתקיים צריכות להיות מוזזות ימינה ביחס לשורה הראשונה במבנה. הקוד התקין:
age = input(‘Insert age; -1 to stop: ‘)
while age != ‘-1’:
print(age)
age = input(‘Insert age; -1 to stop: ‘)
(1) מבוא
העיסוק העיקרי במדעי המחשב הוא פתרון בעיות. בלימוד תכנות אנו כותבים תכניות מחשב (computer programs) הפותרות מגוון של בעיות. נתבונן לדוגמה בבעיית עלויות הספרים:
החנות “ספרים הם החיים” הכריזה על מבצע: מי שיקנו שלושה ספרים יקבלו הנחה על הסך הכולל של הקנייה. שווי ההנחה יהיה מספר שלם אקראי – בין 11 ל-20 ש”ח. לקוחה מסוימת אחת רכשה בחנות שלושה ספרים. יש לכתוב תכנית הקולטת את עלויות שלושת הספרים, מחשבת את התשלום ששילמה הלקוחה, ומודיעה מה תשלום זה. למשל אם שלוש העלויות שנקלטו הן 70, 80 ו-65, וההנחה שהוגרלה היא 17, הלקוחה תשלם 17 – 65 + 80 + 70, כלומר 198 ש”ח.
בבואנו לכתוב תכנית מחשב הפותרת בעיה זו, אנו צועדים בדרך שיש לה שני חלקים עיקריים.
החלק הראשון, חלק הקודם לכתיבת התכנית, הוא תכנון התכנית. בחלק זה אנו מעצבים את האלגוריתם (algorithm), או המתכון, שבאמצעותו נפתור את הבעיה ושעל פיו נכתוב את התכנית. כל אלגוריתם מורכב ממספר צעדים. אלגוריתם אפשרי למימוש תכנית עלויות הספרים מורכב משבעת צעדים אלה:
(1) קליטת עלות ספר אחד שקנתה הקונה
(2) קליטת עלות ספר שני שקנתה הקונה
(3) קליטת עלות ספר שלישי שקנתה הקונה
(4) חישוב סכום העלויות של שלושת הספרים
(5) הגרלת מספר שלם אקראי בין 11 (כולל) ל-20 (כולל)
(6) הפחתת המספר האקראי מסכום העלויות של שלושת הספרים
(7) הודעה על התשלום הסופי
בחלק השני נכתוב את התכנית בשפת תכנות (programming language) מסוימת, לפי האלגוריתם שתכנַנו.
האלגוריתם הוא אפוא הכרחי לכתיבת התכנית, אך אין די בו. כדי לכתבה יש להכיר את שפת התכנות שהתכנית תיכתב בה. ובשלב הראשון של למידת שפת תכנות, יש להכיר את אבני הבניין היסודיות של תכניות הכתובות בה. זו מטרתו העיקרית של פרק זה. לקראת סוף הפרק נישָעֵן על הידע המוקנה בו כדי לכתוב את תכנית עלויות הספרים.
(2) מספרים
תכניות פייתון רבות מטפלות בערכי מספר (numerical values) או בלשון קיצור: במספרים. המספרים שתכניות מחשב מטפלות בהם יכולים להיות מסוגים מגוונים. שני סוגים שכיחים הם מספרים שלמים ומספרים עשרוניים. דוגמות למספרים שלמים:
5 4 333 0 -4 -95353
שלא כמו במספרים שלמים, במספרים עשרוניים יש נקודה עשרונית ומימין לה סִפרה אחת או יותר. דוגמות למספרים עשרוניים:
5.0 7.9 -8.3432 0.0 5343.01
פייתון מאפשרת כתיבות מקוצרות של מספרים עשרוניים מסוימים. מספר עשרוני הגדול מ-1– (מינוס אחת) וקטן מ-1 אפשר לכתוב בלי 0 משמאל לנקודה העשרונית. כך למשל את שלושת המספרים האלה:
0.543 0.8 -0.999
אפשר לכתוב גם כך:
.543 .8 -.999
מספר עשרוני שמימין לנקודה העשרונית בו יש 0, אפשר לכתוב ללא 0 זה. לדוגמה את שלושת המספרים האלה:
3.0 7.0 -9.0
אפשר לכתוב גם כך:
3. 7. -9.
להלן נתוודע לשיטות קיצור נוספות בפייתון, ונכיר כי קיצור הוא אחד מסממניה הבולטים של שפת תכנות זו.
(3) מחרוזות
סוג בסיסי אחר של ערכים (values) המשמשים בתכניות פייתון הוא טקסטים, כלומר רצף של 0 או יותר תווים. בפייתון נהוג לכנות ערך שהוא טקסט בשם ‘מחרוזת טקסט’ (text string), או בקיצור: ‘מחרוזת’ (string). הנה שלוש דוגמות למחרוזות – כל מחרוזת בשורה נפרדת:
It’s 2023?
I have a dream!
Tomorrow is another day…
המחרוזת הראשונה כאן מורכבת מעשרה תווים, בסדר זה: t, I, ‘ (גרש), s, (רווח), 2, 0, 2, 3, ?
בכתיבה מפורשת של מחרוזת בתוך התכנית נכתוב את התווים שההמחרוזת מורכבת מהם, וגם נתחום אותה בגרש מכל צד או במירכאות (נהוג להשתמש בגרש מכל צד כשהמחרוזת קצרה). דוגמות: ע
‘It’s 2021?’
‘I have a dream!’
“Tomorrow is another day…”
הגרש הפותח והגרש הסוגר אינם נכללים ברצף התווים שהוא המחרוזת, וכך הדבר גם בנוגע למירכאות התוחמות מחרוזת. כל ייעודם הוא לציין ערך מסוג מחרוזת.
אין חובה שיהיו תווים במחרוזת. המחרוזת הזאת (גרש צמוד לגרש):
,,
וגם המחרוזת הזאת (מירכאות צמודות למירכאות):
“”
מכונות שתיהן ‘מחרוזת ריקה’ (empty string).
נשים לב כי תו במחרוזת הוא למעשה מחרוזת באורך 1. הנה לדוגמה ארבע מחרוזות באורך 1:
‘a’
‘?’
‘2’
‘ ‘
כל המחרוזות שהוצגו בסעיף זה נפרשות על שורה אחת בלבד. אפשר לפרוש מחרוזת אחת על פני שתי שורות או יותר. דיון בכך מופיע בנספח א’.
(4) ערכים, סוגים של ערכים, ושמות של סוגי ערכים
מספרים שלמים, מספרים עשרוניים ומחרוזות הם רכיבים של השפה המכונים ערכים (values). בפייתון מוגדרים סוגים (או: טיפוסים, types) נוספים ורבים של ערכים, ולאחדים מהם נתוודע בפרקים הבאים. בשפה יש שמות לכל אחד מסוגי הערכים המוגדרים בה, ובהם גם לסוגים שכבר פגשנו:
• שם הסוג של מספרים שלמים הוא int
• שם הסוג של מספרים עשרוניים הוא float
• שם הסוג של ערכי מחרוזת הוא string
להיכרות עם שמות סוגי הערכים יש חשיבות בכמה היבטים של התכנות, ובהם הבנת הודעות שגיאה. דוגמות לדברים יובאו להלן.
(5) ביטויים חשבוניים
נשוב עתה למספרים ונתמקד בחישובים במספרים. בפייתון יש שיטות מגוונות לביצוע חישובים מעין אלה. השיטה הפשוטה ביותר היא כתיבת ביטוי חשבוני (arithmetical expression).
ביטויים חשבוניים מורכבים ממספרים ומסימני חשבון (arithmetical operators). עם סימני החשבון נמנים:
+ מציין חיבור
– מציין חיסור
* מציין כפל
/ מציין חילוק
** מציין חזקה
דוגמות לביטויים אריתמטיים:
3+5
4–100
5.7*9
8.3/2
2**3
בכתיבת ביטויי חשבון שמעורבים בהם מספרים שלמים אפשר להשתמש בשני סימני חשבון נוספים, כדלקמן:
// משמש לחישוב מנה מפעולת חילוק
% משמש לחישוב שארית מפעולת חילוק
לדוגמה תוצאת הביטוי הזה:
7%5
היא 2, כיוון שבחלוקת 7 ב-5 מתקבלת מנה 1 ושארית 2. ומטעם שווה תוצאת הביטוי הזה:
7//5
היא 1.
כל ביטוי שמופיע בו לפחות מספר עשרוני אחד – תוצאתו היא מספר עשרוני. לדוגמה תוצאת הביטוי הזה:
3+5.7
היא 8.7. ותוצאת הביטוי הזה:
3+5.0
היא 8.0.
בדוגמות אלה לביטויים חשבוניים, רכיבי הביטויים – המספרים וסימני החשבון – צמודים אלה לאלה, כלומר אין רווחים המפרידים ביניהם. כדי להקל על ההבנה של ביטויים כאלה וככלל של הוראות שנדון בהן, נוסיף רווחים לקוד. למשל את הביטוי האחרון נכתוב כך:
3 + 5.0
הוספת הרווחים אינה משפיעה על פשר הקוד או על ביצועו, אך תורמת מאוד לקריאוּת התכנית.
באשר לסדר פעולות החשבון: חזקה קודמת לכפל ולחילוק, ואילו כפל וחילוק קודמים לחיבור וחיסור. לדוגמה תוצאת הביטוי הזה היא 13– (מינוס 13):
3 – 2 * 2 ** 3
כדי לשנות את סדר הפעולות אפשר להכניס סוגריים עגולים לביטוי. לדוגמה תוצאת הביטוי הזה היא 8:
((3 – 2) * 2 ) ** 3
שלא כמו בביטוי ללא הסוגריים, כאן פעולת החיסור מתבצעת לפני פעולת הכפל, ופעולת הכפל מתבצעת לפני פעולת החזקה.
נשים לב שמספר ההופעות של הסימן ) בביטוי האחרון שווה למספר ההופעות של הסימן ( בביטוי זה. לולי שיוויון זה היה הביטוי מכיל שגיאת תחביר (syntax error). דוגמה:
(3 – 2) * 2 ) ** 3
כאן חסר הסימן ) בראש הביטוי. נסיון להריץ את הביטוי ייכשל, ופייתון תודיע על סיבת הכשלון:
SyntaxError: invalid syntax
עניין אחרון וחשוב הנוגע לסימני חשבון ויש להדגישו הוא זה: על אף שפעולתם בפייתון דומה מאוד לפעולתם המוכרת לנו מלימודי המתמטיקה, זו וזו אינן שוות לגמרי. למשל בפרק החמישי נראה כי לסימן + יש תפקוד נוסף בשפה השונה מתפקודו בהקשר של פעולות חשבון. ככלל גם אם אופי פעולתה של הוראה בפייתון נראה לנו מובן מאליו כיוון שאנו מכירים פעולה דומה או פעולה בשם זהה מנסיוננו בהקשר אחר, אף פעם איננו יכולים להניח שההוראה בפייתון תפעל לפי הציפיות הקודמות שלנו או רק לפיהן. לכל הוראה בפייתון יש פעילות מוגדרת היטב ועלינו להכירה אם ברצוננו להשתמש בהוראה.
(6) משתנים והוראות השמה
(6.1) מטרות, תחביר, ועקרונות שימוש
משתנה (variable) הוא מנגון בשפת תכנות. יש לו כמה ייעודים. ייעוד עיקרי הוא שמירת ערכים ובייחוד ערכים שהם תוצאות של חישובים, לשם שימוש בהם בעת הצורך. לדוגמה נניח שאנו כותבים תכנית המשווה בין ממוצע ציונים של כל אחת ואחת מ-200 הסטודנטיות בפקולטה, לממוצע הציונים השנתי של כל הסטודנטיות בפקולטה. התכנית מבצעת 200 פעולות השוואה. ברור כי כל השוואה תובעת שיהיה בידינו ממוצע הציונים השנתי של כל הסטודנטיות, וברור כי חישוב ממוצע זה דורש לקלוט את כל הציונים של הסטודנטיות ולחשב את הממוצע. במקום לקלוט את כל הציונים ולחשב את ממוצע הציונים השנתי של כל הסטודנטיות 200 פעמים – פעם אחת עבור כל פעולת השוואה – נרצה לחשב אותו פעם אחת, לשמור את התוצאה, ולהשתמש בה בכל השוואה והשוואה.
לשמירת מספרים, וככלל לשמירת ערכים, נשתמש במשתנים ובהוראות השמה או הוראות הצבה (assignment operations). הנה דוגמה להוראת השמה פשוטה:
avGrade = 87.5
הסממן הצורני המזהה של הוראת השמה הוא הסימן = . והנה שוב דוגמה לכלל שניסחנו בסוף הסעיף הקודם: אנו מכירים את הסימן = ואנו רגילים שהוא מציין זהות, כלומר שדבר אחד שווה לדבר אחר. בשיעורי מתמטיקה ראינו למשל ביטוי זה:
x = 2y
בעינינו הסימן = ציין כאן ש-x זהה ל-2y. לא כך פשרו של הסימן = בהקשר הנוכחי. השימוש בסימן = בפייתון אין פירושו שלפנינו שני דברים השווים זה לזה. לא ולא! כאן הסימן מציין פעולה. מהי פעולה זו?
נעיין שוב בדוגמה להוראת ההשמה:
avGrade = 87.5
נציין ראשית כי הקריאה של הוראת השמה היא מימין לשמאל, כלומר מהצד הימני של הסימן = לצדו השמאלי. בצד הימני של הוראת השמה מופיע ערך או חישוב שהתוצאה שלו היא ערך. בדוגמה מופיע הערך 87.5 (מספר עשרוני). בצד השמאלי של הוראת השמה נציין שם של משתנה. כאן בחרנו בשם avGrade והיינו יכולים לתת שם אחר. לקראת סוף סעיף זה נדון בכללים ובמוסכמות בקביעת שמות למשתנים.
אפשר לראות בפעולה הנעשית בהוראת השמה הצמדה של תווית או כינוי (alias) – שם המשתנה המופיע בצד השמאלי – לערך המופיע בצד הימני; הכינוי המוצמד יאפשר לגשת לערך בתכנית. למשל אם 87.5 הוא תוצאת חישוב ממוצע הציונים של כל הסטודנטיות, אז בכל פעם שנרצה להשתמש בתוצאת החישוב הזאת בתכנית נכתוב avGrade .
אפשר גם לתפוש את הנעשה בהוראת ההשמה לפי שמהּ – היא שָֹמָה דבר אחד, הערך המופיע בצד הימני שלה, בתוך דבר אחר, משתנה – ולדמות את המשתנה ללוח ששמו avGrade ושבכל רגע יכול להופיע בו רק ערך אחד:
דוגמה נוספת – השמת ערך מסוג מחרוזת בתוך משתנה ששמו הוא s:
s = ‘something’
אפשר לדמות את ביצוע ההוראה הזאת ליצירת לוח ששמו s ובו כתובה המחרוזת ‘something’:
אפשר להחליף את הערך שיש בְמשתנה באמצעות הצבת ערך אחר בו. לדוגמה:
avGrade = 87.5
avGrade = 93
במונחים של האנלוגיה, כאן כתבנו 87.5 בלוח avGrade, ולאחר מכן מחקנו ערך זה מהלוח וכתבנו 93 במקומו (כזכור בכל רגע ורגע יכול להיות בלוח ערך אחד בלבד). לאחר הצבת הערך החדש (93) אי אפשר להשתמש במשתנה avGrade כדי לגשת לערך שהוצב בו קודם (87.5).
בנקודה זו נעיר שתי הערות חשובות.
ראשית, תפיסה זו של הוראת השמה בתור השמה או הצבה של ערך במקום כלשהו וההתנסחויות “ערך מוצב במשתנה” ו”הערך שיש במשתנה” אינן מדויקות. בכל זאת יש בהן תועלת בשלבים הראשונים של למידת תכנות, ולכן נשתמש בהן במה שלהלן.
שנית, שלא כמו בשפות תכנות אחרות, בהגדרת משתנה בפייתון אין קובעים את סוג הערכים שאפשר להציב במשתנה. לדוגמה הגדרה זו של המשתנה s:
s = ‘something’
היא בלתי תקינה בשפות תכנות מסוימות, הדורשות לקבוע בהגדרה המשתנה s שהוא יכול להכיל אך ורק ערכים מסוג מסוים, למשל מחרוזת. בפייתון אפשר להציב במשתנה אחד ערכים מסוגים מגוונים, וסוג הערך המוצב נקבע בשעת ההצבה. מנגנון זה מכונה ‘קביעת סוג (או: טיפוס) דינמית’ (dynamic typing). דוגמה:
s = ‘something’
s = 7
כאן הצבנו במשתנה s את המחרוזת ‘something’ ואחר כך הצבנו בו את המספר השלם 7.
כאמור סדר הביצוע של הוראת השמה הוא תמיד מימין לשמאל – הערך מימין מוצב במשתנה משמאל – ולא להפך. לדוגמה ההוראה הזו היא שגויה:
3 = x
לפי כללי התחביר של השפה, מתבצע כאן ניסיון להצבת ערך שיש במשתנה – בתוך ערך, ולפעולה זו אין משמעות בשפה.
הערך המוצב במשתנה יכול להיות תוצאה של חישוב. דוגמה:
avGrade = (88 + 93 + 63 + 100 + 45 + 75 + 66 + 89) / 8
לפנינו הוראת השמה. בצדה הימני יש ביטוי חשבוני שתוצאתו 77.375. אפשר לשער שביצוע ההוראה יוליך לכתיבת הביטוי כולו ב”לוח” שהוא המשתנה avGrade, אך אין זה כך. במשתנה תוצב אך ורק תוצאת הביטוי, כלומר 77.375.
מן הרגע שערך מוצב במשתנה, נוכל לגשת לערך זה באמצעות ציון שם המשתנה. דוגמה:
grade = 95
avGrade = (88 + 93 + 63 + 100 + 45 + 75 + 66 + 89) / 8
diff = grade – avGrade
בהוראה הראשונה הצבנו את הציון 95 במשתנה בשם grade. בהוראה השנייה הצבנו ממוצע של 8 ציונים במשתנה בשם avGrade; ערך הממוצע הוא 77.375. בהוראה השלישית הצבנו במשתנה ששמו diff ערך שהוא תוצאה של ביטוי חשבוני: הערך המוצב במשתנה grade פחות הערך המוצב במשתנה avGrade, הנה במפורש הביטוי החשבוני המתבצע בצד הימני של ההוראה השלישית עבור הערכים שהוצבו בשני המשתנים האלה:
95 – 77.375
התוצאה, 17.625, מוצבת במשתנה diff.
נשים לב שאי אפשר להשתמש במשתנה טרם שהוגדר. לדוגמה אם נריץ קוד זה:
a = 3
b = c + 3
נקבל הודעת שגיאה זו:
b = c + 3
NameError: name ‘c’ is not defined
הוראת ההשמה השגויה מנסה לגשת לתכנו של משתנה שטרם הוגדר, c. כדי שהקוד ירוץ באופן תקין נגדיר משתנה זה באמצעות הוראת השמה, למשל כך:
c = 5
b = c + 3
שם משתנה אחד יכול להופיע משני צדיה של הוראת השמה. דוגמה:
grade = 10
grade = grade + 10
משמעותה של ההוראה השנייה בקוד אינה זו –
10 = 10 + 10
כבר ראינו כי מה שמופיע בצד השמאלי של ההוראה הוא כינוי לערך המופיע בצד הימני, וכאמור גם מקום שבו מושם הערך הזה. לכן בביצוע הוראת השמה זו:
grade = grade + 10
ראשית מחושב הביטוי בצד הימני, 10 + 10. ותוצאתו מוצבת במשתנה grade. הערך החדש, כלומר 20, מחליף את הערך הקודם שהיה במשתנה grade, כלומר 10, ולא נוכל עוד להשתמש במשתנה grade לשם גישה לערך הקודם שהוצב בו.
נעיר לסיום כי באמצעות האופרטור =+ אפשר לקצר את ההוראה הזאת:
grade = grade + 10
ולכתוב כך:
grade += 10
שתי ההוראות שקולות זו לזו ובסופן יוצב ערך שווה במשתנה grade. בדומה נוכל להשתמש באופרטורים =-, =*, =/ ו-=** לקיצור הוראות השמות במשתנה תוצאות של ביטויים חשבוניים המפעילים את האופרטורים -, * . / ו-** על המשתנה עצמו. עם זאת יש לתת את הדעת כי עבור סוגים מסוימים של ערכים, תוצאותיהן של הוראות מקוצרות הנכתבות באמצעות האופרטור =+ אינה זהה לתוצאותיהן של ההוראות בגרסתן הלא-מקוצרת. לעניין זה עיינו בנספח ב’ לאחר לימוד הפרק החמישי.
(6.2) כללים ומוסכמות בקביעת שמות משתנים
במתן שמות למשתנים יש כללים ויש מוסכמות. כאן נסתפק בציון שלושה כללים אלה:
(1) שמות משתנים יכולים להכיל אך ורק אותיות באנגלית, ספרות או קו תחתון (כלומר הסימן _).
(2) שמות משתנים אינם יכולים להתחיל בספרה.
(3) שמות משתנים אינם יכולים להיות מילים שמורות בשפה או שמות של פונקציות בשפה.
הנה דוגמה להוראה המגדירה משתנה ששמו אינו עולה בקנה אחד עם הכלל הראשון:
my Str = “space is forbidden”
בצד השמאלי של הוראת השמה צריך להיות שם משתנה. כאן רצינו לתת את השם myStr ובטעות כתבנו רווח בתוך השם. תו רווח אינו יכול להיות אחד התווים בשם משתנה.
והנה דוגמה להוראה המגדירה משתנה ששמו אינו מתיישב עם הכלל השלישי:
if = 3
כפי שנראה בפרק הבא, המילה if היא מילה השמורה בשפה לביצוע פעולה מסוימת (התניה). לכן בלתי אפשרי להשתמש בה בתור שם משתנה.
בשמות משתנים מבדילים בין אותיות ‘גדולות’ (upper case) לאותיות ‘קטנות’ (lower case). בתור דוגמה לדברים נתבונן בקוד הזה:
grade = 5
Grade = 7
בקוד זה מוגדרים שני משתנים שונים זה מזה. כל האותיות בשמותיהם שוות חוץ מהאות הראשונה: במשתנה אחד היא g ובמשתנה האחר היא G. אם כן בסוף ביצוע הקוד הערך בתוך המשתנה grade הוא 5 ולא 7.
בכל הנוגע למוסכמות במתן שמות משתנים, אחת מהן היא שרצוי לתת למשתנה שם שיש לו קשר לערך שהוא מכיל. כך נהגנו בדוגמות לעיל, וכך ננהג גם להלן. שיטה זו מועילה מאוד בקריאות הקוד, עניין שנרחיב בו את הדיון להלן. חשוב לציין עם זאת שהיא אינה הכרחית לתקינות הקוד. אם אנו כותבים תכנית המציבה ציון במשתנה, נוכל לכתוב כך:
grade = 83
ונוכל לכתוב גם כך:
xyz = 183
ושתי ההוראות ירוצו באופן תקין.
מוסכמה אחרת מזו קשורה בשמות המורכבים משתי מילים או יותר. יש שתי דרכים מקובלות לכתיבת שמות מעין אלה: הפרדה בין המילים בקו תחתון, או הצמדת המילים ופתיחת כל מילה החל מהמילה השנייה באות גדולה. למשל את ההוראה הזאת:
stud_grade = 95
יש מי שיכתבו כך:
studGrade = 95
הפרדת המילים באמצעות קו תחתון היא הרצויה מבחינת המקובל בפייתון, ואולם היא אינה הכרחית לתקינות הקוד. מכל מקום יש להקפיד על עקיבות (consistency) בשיטה שנוקטים בה.
(7) פונקציות
רכיב יסודי נוסף של תכניות פייתון הוא הוראות המפעילות פונקציות (functions).
גם ‘פונקציה’ הוא מונח המוכר לכם מהקשרים אחרים, ובייחוד מלימוד מתמטיקה. למשל ראיתם ודאי פונקציה זו:
f(x) = x2
אופיין של פונקציות המשמשות בשפת תכנות והשימוש בהם דומה לאופיין של פונקציות במתמטיקה, אך גם שונה ממנו.
בהפשטה, פונקציה המשמשת בשפת תכנות היא קטע של קוד (code), כלומר רצף של הוראות בשפה. כל פונקציה מבצעת פעולה מסוימת לפי נתונים מסוימים. למשל יכולה להיות פונקציה המקבלת מספר בתור נתון ומחשבת את ריבוע המספר, ויכולה להיות פונקציה המקבלת בתור נתון שם של קובץ וקוראת מחרוזת מהקובץ הזה.
לכל פונקציה בשפת תכנות יש שם. למשל לפונקציה המחשבת ריבוע של מספר יכול להיות השם power, ולפונקציה הקוראת מחרוזת מקובץ יכול להיות השם readFile.
המטרה העקרונית העיקרית של קיום פונקציות בתכנית היא לאפשר לנו לבצע סדרת הוראות מסוימת פעמים רבות בתכנית בלי לכתוב את סדרת ההוראות מחדש בכל פעם שאנו נדרשים לבצעה. נניח למשל שהתכנית שלנו צריכה לקרוא מחרוזות מ-1000 קבצים. קריאת מחרוזת מקבצים כרוכה בביצוע סדרה מסוימת של הוראות. במקום לכתוב שוב ושוב את סדרת ההוראות הזאת, נכתוב אותה פעם אחת, נגדיר אותה בתור פונקציה ובייחוד ניתן לה שם. נפעיל את הפונקציה הזאת בכל פעם שנרצה לקרוא מחרוזת מקובץ.
בפרק העשירי נרחיב את הדיבור בעניין הייעוד הכללי של שימוש בפונקציות ונראה כיצד לכתוב פונקציות בעצמנו (user functions). כאן נתמקד בפונקציות מערכת (built-in functions), פונקציות אלו הן מוּבְנות בליבת השפה או מיובאות לתכנית. נתוודע לכמה פונקציות חשובות מעין אלו. אין אנו צריכים לראות את קטעי הקוד המרכיבים אותן, אך עלינו להבין מה הנתונים שלהן, מה הן עושות, ומה תוצאות הפעולות שלהן. אגב הדיון נלמד מה התחביר הכללי של קריאה לפונקציה, ומה פירוש החזרת ערך מפונקציה.
(7.1) הדפסת ערכים ל-shell – הפונקציה print
בפייתון מוגדרת הפונקציה print. פונקציה זו מבצעת פעולה שכיחה: הדפסת ערכים ל-shell. נעיין בדוגמה ראשונה לשימוש בה:
print(‘Hello World’)
לפנינו זימון פונקציה (או: קריאה לפונקציה, function call).
הזימון כאן, כמו זימון של כל פונקציה, פותח בשם הפונקציה. מימין לשם בא סוגר עגול פותח ואחריו סוגר עגול סוגר. בזימון כאן יש ערך (מחרוזת) בתוך הסוגריים; כפי שנראה בזימוני פונקציות יכולים להיות גם סוגריים ריקים, או יותר מערך אחד. נכנה כל ערך הבא בתוך הסוגריים העגולים בזימון פונקציה בשם “ארגומנט” (argument). למשל כאן נאמר ש”הזימון מעביר לפונקציה print ארגומנט אחד: המחרוזת ‘Hello World'”. ארגומנטים הם נתונים שהפונקציה מקבלת ונזקקת להם כדי לבצע את פעולתה באופן תקין.
הפונקציה print מדפיסה את הארגומנט שהיא מקבלת ל-shell. הזימון למעלה ידפיס ב-shell את המחרוזת ‘Hello World’:
print(‘Hello World’)
>>>
Hello World
כאן ובמה שלהלן נכתוב מתחת לדוגמות הקוד את תוצאת ההדפסה של ההוראה print, וככלל את הפלט (output) המודפס ל-shell. מעל לפלט נכתוב שלושה סוגריים משולשים סוגרים.
תנו דעתכם כי בהדפסת מחרוזת ל-shell אין מודפסים גרשיים או מירכאות התוחמים אותה.
נוסף להדפסת הערך המועבר אליה, הפונקציה print גם יורדת שורה ב-shell. לדוגמה, שתי ההוראות האלה יגרמו להדפסת המספרים 3 ו-5 בשתי שורות נפרדות ב-shell.
print(3)
print(5)
>>>
3
5
הפונקציה print יכולה להדפיס ערכים מסוגים מגוונים, כגון מספרים שלמים שליליים, מספרים עשרוניים, ומחרוזות. דוגמות:
print(-753)
print(6.934)
print(‘print me’)
>>>
-753
6.934
print me
אפשר לתת לפונקציה print משתנים והיא תדפיס את תכנם. דוגמה:
grade = 83
print(grade)
>>
83
תנו דעתכם כי הקוד הזה:
grade = 92
print(‘grade’)
ידפיס את המחרוזת ‘grade’ ולא את המספר 92. בה בעת הקוד הזה:
grade = 92
print(grade)
ידפיס את המספר 92 ולא את המחרוזת ‘grade’.
הוראת ה-print בקוד הזה תדפיס את הערך הנוכחי המוצב במשתנה grade, כלומר 93, אך לא את הערך הקודם שהוצב בו, כלומר 51.
grade = 51
grade = 93
print(grade)
>>
93
אפשר גם לתת לפונקציה print ביטויים שיש להם תוצאה, כגון ביטויים אריתמטיים. היא תדפיס את התוצאה של הביטויים. לדוגמה זימון זה של הפונקציה print ידפיס 8:
print(3 + 5)
>>>
8
שימו לב לסדר ביצוע הפעולות כאן: ראשית מתבצע החישוב בתוך הסוגריים העגולים – הביטוי 3 + 5. רק בגמר החישוב תוצאתו מועברת לפונקציה print והיא מבצעת את ההדפסה ל-shell.
אפשר לזמן את הפונקציה print ולא לתת לה ארגומנטים כלל. לדוגמה:
print()
אם נזמן את הפונקציה print כך, נקבל ב-shell ירידת שורה ותו לא. לכן זוג ההוראות הזה יגרום לירידת שורה ב-shell, הדפסת 5, וירידת שורה נוספת.
print()
print(5)
>>>
5
גם אפשר לזמן את print ולתת לה יותר מארגומנט אחד. הארגומנטים המופיעים בסוגריים העגולים מופרדים זה מזה באמצעות התו פסיק. ב-shell הם מוצגים כולם בשורה אחת, ורק לאחר הצגת האחרון מתבצעת ירידת השורה. דוגמה:
print(3, 5, 7)
>>>
3 5 7
כפי שאפשר לראות ב-shell הארגומנטים המודפסים מופרדים זה מזה באמצעות תו רווח.
נעיר לסיום בנוגע לפעולת הפונקציה print, כי תיאור מדויק יותר שלה מהתיאור שהובא לעיל הוא זה: היא מדפיסה ל-shell מחרוזת שהיא מקבלת, ולצדה מחרוזת נוספת שהיא אינה מקבלת במפורש; ברירת המחדל היא שהמחרוזת הנוספת הזאת היא מחרוזת שהדפסתה יורדת שורה (המחרוזת ‘n/’). אפשר לשנות את ברירת המחדל אם קובעים במפורש את המחרוזת הנוספת הזאת. כך:
print(3, 4, end = “”)
print(5)
>>
3 45
6
הביטוי “” = end הבא ממש לפני הסוגר העגול הסוגר בהוראה הראשונה, מורה לפייתון איזו מחרוזת יש להדפיס לאחר הדפסת הארגומנטים שיש לפני הביטוי – כאן מודפס 3, אחר כך מודפס תו רווח, אחר כך מודפס 4, ואחר כך מודפסת מחרוזת ריקה (ולא המחרוזת ‘n/’). ההוראה השניה מדפיסה 5 בצמידות ל-4, כיוון שההוראה הקודמת לא הדפיסה את המחרוזת ‘n/’. לאחר הדפסת המספר 5 מודפסת המחרוזת ‘n/’ ומתבצעת ירידת שורה. כך ההוראה השלישית מדפיסה 6 בשורה נפרדת.
(7.2) קליטת ערכים מה-shell – הפונקציה input
רוב התכניות שנכתוב יטפלו בקלט שניתֵן להן. הקלט הזה יהיה נתונים שנקבל באמצעות ה-shell או מקבצים. לשאיבת נתונים מקבצים נגיע בפרק השביעי. כאן נראה כיצד לקבל מה-shell נתונים שמכניסה המשתמשת (ה-user).
נניח שאנו רוצים לכתוב תכנית המקבלת מהמשתמשת מחרוזת ומדפיסה את המחרוזת שהוכנסה. כדי לקלוט את המחרוזת מהמשתמשת נעזר בפונקציה input. מבנה הזימון של הפונקציה input דומה למבנה הזימון של הפונקציה print. דוגמה:
input(‘Please enter any string: ‘)
גם כאן הזימון מורכב משם הפונקציה וסוגריים עגולים; בתוך הסוגרים העגולים מופיע ארגומנט אחד המועבר לפונקציה. הארגומנט שמקבלת הפונקציה input הוא מחרוזת; על פי רוב המחרוזת נועדה לתת למשתמשת הנחיות בנוגע לקלט שעליה להכניס. כאן ההנחיה היא להכניס מחרוזת כלשהי.
לאחר שכתבנו את ההוראה והרצנו אותה זרימת התכנית מתקדמת כך.
ראשית מודפסת ב-shell המחרוזת שהועברה לפונקציה input.
בשלב זה התכנית ממתינה שהמשתמשת תקליד דבר-מה במקלדת ואז תלחץ על המקש ENTER. בדוגמה המוצגת המשתמשת הקישה ברצף על המקשים
H
e
l
l
o
בסדר זה.
לאחר הקשה על חמשת המקשים, המשתמשת הקישה על ENTER. הפונקציה input קיבלה את המחרוזת Hello מהמשתמשת. או אז הפונקציה input עושה דבר מה נוסף, דבר מה שהפונקציה print אינה עושה: היא מחזירה ערך, ובייחוד מחזירה את המחרוזת שהמשתמשת הכניסה. מה פירוש “מחזירה ערך”?
במושג החזרת ערך מפונקציה נדון בהרחבה בפרק העשירי. כרגע נאמר רק כי הערך שמחזירה פונקציה חוזר למקום שהיא מזומנת ממנו, ואם ברצוננו לשמור אותו עלינו להציב אותו במשתנה. כדי להבהיר את הדברים נעיין בהוראה זו:
someString = input(“Please enter any string: “)
לפנינו הוראת השמה, וככל הוראת השמה ביצועה מתחיל בצד הימני. כאמור אנו מניחים שהזימון של הפונקציה input כאן מקבל מהמשתמשת את המחרוזת Hello. בסיום פעולת input המחרוזת הזאת מוחזרת למקום שהזימון נעשה בו, כלומר לצד ימני של פעולת השמה. מכאן שבסוף ביצוע הפונקציה מתבצעת למעשה הוראת השמה זאת:
someString = ‘Hello’
אם כן המחרוזת Hello מוצבת במשתנה someTring.
תנו דעתכם: לו היינו כותבים את הוראת ה-input בדד, כלומר לא בתור צד ימני של הוראת השמה:
input(“Please enter any string: “)
הפונקציה הייתה קולטת את המחרוזת שהכניסה המשתמשת, אך מחרוזת זו לא הייתה נשמרת בתכנית והיה בלתי אפשרי לגשת למחרוזת זו בתכנית.
זה המקום להעיר כמה הערות חשובות. ראשית אין הכרח לתת מחרוזת בתור ארגומנט לפונקציה input. הקוד הזה תקין לחלוטין:
someString = input()
עם זאת בדרך כלל נעביר מחרוזת לפונקציה input כדי לסייע למשתמשת להבין את אופי הקלט המבוקש.
גם שימו לב שבדוגמה שהובאה לעיל לקליטת מחרוזת באמצעות הפונקציה input, המשתמשת הכניסה מחרוזת בלי גרשיים או מירכאות התוחמים אותה, כלומר היא כתבה Hello ולא כתבה ‘Hello’. אם הייתה כותבת את הגרשיים הם היו נחשבים חלק מהמחרוזת הנקלטת: הפונקציה input הייתה מקבלת את המחרוזת “‘Hello'” ומחזירה ערך מחרוזת זה.
ועוד זאת: תנו דעתכם לקוד זה –
age = input(“Please enter your age: “)
זימון זה של הפונקציה input מנחה את המשתמשת להכניס מחרוזת המציינת גיל. המשתמשת יכולה למלא את ההנחיה כלשונה, אך היא יכולה לסטות ממנה (על פי רוב לא במכוון). אם למשל היא תכניס כאן את המחרוזת ‘Europe’ (בלי הגרשיים) התכנית תרוץ באופן תקין. אי ההלימה בין ההנחיה לבין המחרוזת שהוכנסה ולשם המשתנה לא בהכרח יגרמו להרצת התכנית להיכשל. מכל מקום כאן ולהלן נניח כי המשתמשת פועלת בנאמנות למה שמבקשים ממנה, אלא אם כן נציין במפורש אחרת.
נשוב ונדגיש שהערכים המוחזרים מהפונקציה input הם תמיד מחרוזות. כפי שנראה עתה הגדרה זו של ערכי ההחזרה שלה מחייבת לעתים להמיר ערכים אלה לערכים אחרים לפני שימוש בהם.
(7.3) המרות – הפונקציות float, int ו-str
נעיין בקטע קוד זה:
grade = input(‘Please enter grade before factor: ‘)
print(‘Grade with factor is: ‘, grade + 5)
הקטע מתחיל בקליטת ערך מהמשתמשת. לפי ההנחיה ערך זה צריך לייצג ציון. הערך הנקלט מוצב במשתנה grade. ההוראה השנייה מדפיסה את הערך שנקלט ולצדו תוצאה של פעולת חשבון: הערך שנקלט ועוד 5.
נריץ את קטע הקוד. המשתמשת תקליד על צירוף המקשים 90 (כלומר 9 ואחר כך 0), ונצפה כי לאחר שתלחץ על ENTER יודפס ב-shell המספר 95, אך אבוי – לא כך יקרה! במקום זה נקבל הודעת שגיאה כדלקמן:
print(‘Grade with factor is: ‘, grade + 5)
TypeError: must be str, not int
מה פשר הודעה זאת? מה היא שגיאה מסוג TypeError? ומה הכוונה ב ‘must be str, not int’?
הערכים שמחזירה הפונקציה input הם תמיד מחרוזות. כך הזימון של הפונקציה input שהרצנו כאן אינו קולט את המספר 90 אלא את המחרוזת ’90’. מחרוזת זו מוצבת במשתנה grade. לכן כשפייתון באה לבצע את הוראת ה-print היא רואה שאנו מבקשים שתדפיס את תוצאת הביטוי:
grade + 5
והיא אינה מבינה מה רצוננו שתבצע: הרי אנו מבקשים ממנה לחבר מחרוזת למספר, כלומר לבצע את הפעולה הזאת:
’90’ + 5
ובפייתון אין משמעות לפעולה מעין זו.
כדי שקטע הקוד יפעל באופן תקין עלינו להמיר את המחרוזת שהמשתמשת מכניסה למספר – מספר שלם או מספר עשרוני – קודם לביצוע פעולת החיבור.
המרת מחרוזת למספר שלם מתבצעת באמצעות הפונקציה int: פונקציה זו מקבלת, בתור ארגומנט, מחרוזת המציינת מספר שלם, והיא מחזירה את המספר השלם הזה. הנה כך נשכתב את הקוד למעלה באמצעות הפונקציה int כדי שהקוד יפעל באופן תקין
grade = input(‘Please enter grade before factor: ‘)
intGrade = int(grade)
print(‘Grade with factor is: ‘, intGrade + 5)
ההוראה השניה כאן מבצעת את ההמרה. זו הוראת השמה. בצדה הימני מזומנת הפונקציה int והיא מקבלת את המחרוזת שהוצבה במשתנה grade. היא מחזירה את המספר השלם שהמחרוזת מייצגת: אם המשתמשת הכניסה את המחרוזת ’90’ מוצב המספר השלם 90 במשתנה intGrade. הביטוי החשבוני המחושב בהוראת ה-print יתבצע באופן תקין כיוון שהן הערך שיש במשתנה intGrade הן 5 הם מספרים.
אפשר לכתוב את הקוד המשוכתב בשתי הוראות במקום בשלוש, כך:
grade = int(input(‘Please enter grade before factor: ‘))
print(‘Grade with factor is: ‘, grade + 5)
סדר ביצוע הפעולות בצדה הימני של ההוראה הראשונה הוא “מבפנים החוצה”. ביתר פירוט: ראשית מזומנת הפונקציה input ונקלטת המחרוזת המייצגת ציון. הפונקציה input מחזירה מחרוזת זו למקום שממנו זומנה – בין הסוגריים העגולים של זימון הפונקציה int – ולכן המחרוזת הזו ניתנת בתור ארגומנט לפונקציה int, וזו ממירה את המחרוזת למספר שלם. אם כן כאן המשתנה grade יכיל מספר שלם ולא מחרוזת.
כאמור הפונקציה int ממירה למספרים שלמים מחרוזות המציינות מספרים שלמים. אם נעביר אליה בתור ארגומנט מחרוזת שאינה מציינת מספר שלם תתקבל שגיאה. דוגמה:
s = ‘5.3’
num = int(s)
>>>
num = int(s)
ValueError: invalid literal for int() with base 10: ‘5.3’
כיוון ש-s היא מחרוזת שאינה מציינת מספר שלם, זימון הפונקציה int מוליך להודעת שגיאה.
בפייתון יש עוד כמה וכמה פונקציות המבצעות המרות. למשל מחרוזת המציינת מספר עשרוני אפשר להמיר למספר עשרוני. נעשה זאת באמצעות הפונקציה float. דוגמות:
temperature = float(‘36.7’)
הפונקציה float יכולה להמיר מחרוזת המייצגת מספר שלם. בדוגמה זו היא מקבלת מספר שלם וכרגיל מחזירה מספר עשרוני. דוגמה:
myTemperature = float(’36’)
print(myTemperature)
>>>
36.0
גם הכיוון ההפוך אפשרי, כלומר המרת מספר למחרוזת. זה נעשה באמצעות הפונקציה str. היא יכולה להמיר מספר שלם למחרוזת וגם מספר עשרוני למחרוזת. דוגמות:
s1 = str(5)
num = 7.3
s2 = str(num)
בתום ביצוע קוד זה המשתנה s1 מכיל את המחרוזת ‘5’ ואילו המשתנה s2 מכיל את המחרוזת ‘7.3’.
(7.4) בדיקת סוג – הפונקציה type
ראינו כי בהגדרת משתנה בפייתון אין נקבע סוג הערכים שאפשר להציב במשתנה, ובכל שלב ב”חייו” של משתנה אפשר להציב בו ערך מסוג שונה מסוגי הערכים שהוצבו בו קודם (למשל מספר במקום מחרוזת). עולה אפוא השאלה: כיצד נוכל לדעת מה סוגו של הערך הנוכחי המוצב במשתנה? דרך אחת לזהותו היא להשתמש בפונקציה type. הפונקציה מקבלת ערך או משתנה המחזיק ערך ומחזירה את שמו של סוג הערך. דוגמות:
num = 3
print(type(3))
>>>
<class ‘int’>
num = 7.3
print(type(num))
>>>
<class ‘float’>
s = ‘hello’
print(type(s))
>>>
<class ‘str’>
בפרקים הבאים ניווכח כי ליכולת לזהות סוג של ערך נתון באמצעות הפונקציה type (ובאמצעות פונקציה נוספת: isinstance) יש תועלת מיוחדת בכתיבת תנאים לוגיים ובטיפול באוספים של ערכים מסוגים מגוונים.
(8) מודולים
פעמים רבות התכניות שנכתוב ישתמשו בפונקציות הכלולות באוספים ייעודיים של פונקציות. אוספים אלה מכונים מודולים (modules). לכל מודול יש ייעוד מוגדר, כגון טיפול במחרוזות, טיפול בגרפיקה, טיפול בפרוצדורות סטטיסטיות, והפונקציות הכלולות בו נועדו לשרת ייעוד זה. חוץ מפונקציות מודולים יכולים להכיל ערכים קבועים.
נדגים את הדברים באמצעות המודול random. זה מודול המכיל פונקציות המיועדות לטיפול במספרים אקראיים. כדי להשתמש בפונקציות המוכלות בו עלינו לומר לתכנית במפורש שזה רצוננו. נעשה זאת באמצעות ההוראה import, למשל כך:
import random
כאן כתבנו את ההוראה import ולצדה שם המודול שאנו רוצים להשתמש בתכנו.
לאחר ביצוע ההוראה import נוכל לזמן פונקציות המוגדרות במודול, למשל הפונקציה randint. הפונקציה הזאת מקבלת שני מספרים שלמים המגדירים גבולות של טווח מספרים. היא מגרילה מספר שלם אקראי בטווח הזה. הנה כך לדוגמה נראה זימון של הפונקציה randint:
randNum = random.randint(3, 10)
הוראה זו תציב במשתנה randNum מספר שלם אקראי בטווח שבין 3 (כולל) ל-10 (כולל).
נשים לב שמבנה הזימון כאן הוא שונה מעט ממבנה של זימוני פונקציות שראינו עד כה: כתבנו כאן לפני שמה של הפונקציה את שם המודול שהיא נכללת בו (random) ונקודה. נוכל להימנע מתוספת זו אם נייבא לתכנית את המודול random באופן מעט אחר מזה שראינו:
from random import *
אם זה יהיה אופן הייבוא של המודול לתכנית, נוכל לקצר ולכתוב כך:
randNum = randint(3, 10)
כאן כתבנו את שם הפונקציה בלבד, בלי לציין את שם המודול שהיא כלולה בו.
(9) תכנית ראשונה - עלות קניית ספרים
על סמך הידע שקנינו למעלה, נפתור עתה את בעיית עלויות הספרים. הנה היא שוב:
החנות “ספרים הם החיים” הכריזה על מבצע: מי שיקנו שלושה ספרים יקבלו הנחה על הסך הכולל של הקנייה. שווי ההנחה יהיה מספר שלם אקראי – בין 11 ל-20 ש”ח. לקוחה מסוימת אחת רכשה בחנות שלושה ספרים. יש לכתוב תכנית הקולטת את עלויות שלושת הספרים, מחשבת את התשלום ששילמה הלקוחה, ומודיעה מה תשלום זה. למשל אם שלוש העלויות שנקלטו הן 70, 80 ו-65, וההנחה שהוגרלה היא 17, הלקוחה תשלם 17 – 65 + 80 + 70, כלומר 198 ש”ח.
פתרון בעיה זו ובעיות אחרות תובע מאתנו להשיב על כמה שאלות, ובייחוד:
(1) מה הנתונים שהתכנית מעבדת? מה הסוג שלהם (מספר שלם? מספר עשרוני? וכו’)? וכיצד הם נקלטים? כאן: הנתונים הם מחירי שלושת הספרים. כולם מספרים עשרוניים הנקלטים מהמשתמשת.
(2) מה עיבוד הנתונים המתבצע בתכנית? כאן: חישוב סכום מחירי הספרים והפחתת מספר שלם אקראי בין 11 ל-20 מסכום זה.
(3) מהו הפלט של התכנית? כאן: הודעה למשתמשת בדבר העלות הסופית של קניית שלושת הספרים.
על יסוד התשובות לשאלות האלו נְפָתח אלגוריתם או מתכון לפתרון הבעיה. האלגוריתם יורכב מצעדים שעל פיהם ימומשו כל אחד ואחד מחלקי התכנית – קליטת הנתונים, עיבודם והצגת התוצאה (או החזרתה). הנה האלגוריתם שכבר הצענו לעיל:
קליטת הנתונים
(1) קליטת מחיר ספר אחד שקנתה הקונה
(2) קליטת מחיר ספר שני שקנתה הקונה
(3) קליטת מחיר ספר שלישי שקנתה הקונה
עיבוד הנתונים
(4) הגרלת מספר שלם אקראי בין 11 (כולל) ל-20 (כולל)
(5) חישוב סכום המחירים של שלושת הספרים
(6) הפחתת המספר האקראי מסכום המחירים של שלושת הספרים
הצגת (או: החזרת) התוצאה
(7) הודעה על התשלום הסופי
שימו לב שהצעדים באלגוריתם ממוספרים – לכל צעד מספר משלו.
לפי האלגוריתם הזה נממש את התכנית הפותרת את בעיית עלות הספרים.
צעדים 1–3
כדי לקלוט את שלושת המספרים המציינים את עלויות הספרים נשתמש בפונקציות input ו-float. הנה הקוד:
price1 = float(input(‘Price of book 1: ‘))
price2 = float(input(‘Price of book 2: ‘))
price3 = float(input(‘Price of book 3: ‘))
כיוון ששלושת המחירים הם מספרים עשרוניים, המרנו את המחרוזות שקולטת הפונקציה input למספרים עשרוניים באמצעות הפונקציה float. הצבנו את המספרים העשרוניים בשלושה משתנים שונים זה מזה.
צעד 4
כדי להגריל מספר שלם אקראי בין 11 (כולל) ל-20 (כולל) נשתמש בפונקציה randint של המודול random. לא נשכח לייבא את המודול:
import random
reduc = random.randint(11, 20)
נציב את המספר שהוגרל במשתנה reduc.
צעדים 5–7
בהוראה אחת נחשב את סכום עלויות הספרים
sumPrices = price1 + price2 + price3
ונוכל לממש בהוראה אחת את הפחתת המספר האקראי מסכום זה וגם את הצגת התוצאה, כך:
print(‘The price after reduction is: ‘, sumPrices – reduc)
נעיר כי בזימון הוראת ה-print אין צורך עקרוני לתת מחרוזת המסבירה מה המספר המודפס, אך הסבר זה מועיל למשתמשת.
עתה כל שנשאר הוא להרכיב את הקוד שכתבנו לתכנית אחת באופן זה:
import random
price1 = float(input(‘Price of book 1: ‘))
price2 = float(input(‘Price of book 2: ‘))
price3 = float(input(‘Price of book 3: ‘))
reduc = random.randint(11, 20)
sumPrices = price1 + price2 + price3
print(‘The price after reduction is: ‘, sumPrices – reduc)
שימו לב שהוראת הייבוא של המודול random מופיעה כאן בתור ההוראה הראשונה בתכנית. נהוג למקם הוראות import בראש התכנית.
(10) הערות תיעוד וקריאות התכנית
חוץ מכתיבת קוד תקין, בתכנות רצוי לשאוף לקוד מובן – הן למתכנתות ומתכנתים אחרים, הן לנו – כשנקרא את הקוד בעוד זמן מה. כתיבת קוד מובן היא אות לתכנות טוב.
אמצעי עיקרי בהפיכת תכנית מחשב למובנת היא הוספת הערות תיעוד (documentation). הערת תיעוד מסבירה לקוראי התכנית ולקוראותיה דבר מה בנוגע לקוד. בדרך כלל מבחינים בין הערות תיעוד לבין קוד להרצה באמצעות הנחת הסימן # בראש השורות המכילות את הערות התיעוד. לשם דוגמה הנה התכנית שכתבנו בסעיף הקודם בצירוף הערות תיעוד:
#
# Books.py – a program for calculating total price of three
# books.
#
# Authored by Ofer Elior
# Last modified: September 27, 2022
#
import random
# Receiving books’ prices
price1 = float(input(‘Price of book 1: ‘))
price2 = float(input(‘Price of book 2: ‘))
price3 = float(input(‘Price of book 3: ‘))
# Calculating total sum after reduction and printing it.
reduc = random.randint(11, 20)
sumPrices = price1 + price2 + price3
print(‘The price after reduction is: ‘, sumPrices – reduc)
כפי שאפשר להיווכח, נהוג לכתוב בראש תכנית כמה הערות תיעוד ובהן מידע כלל על אודות התכנית, כגון שם הקובץ שהיא נמצאת בו, מה מטרתה הכללית, מי חיברוה, ומה תאריך העדכון האחרון שלה.
מתי יש להוסיף הערת תיעוד, היכן, ועד כמה מפורטת צריכה להיות ההערה? אין תשובות גורפות לשאלות אלו. כתיבת הערות תיעוד היא מיומנות הנרכשת ככל שנצבר נסיון בתכנות וגם תלויה במידת המובנות של הקוד. לעתים נסתפק בהערה אחת קצרה המסבירה מספר שורות קוד, ולעתים נלווה הוראה מפורטת להוראה אחת, כיוון שנאמין שההוראה עשויה לא להיות מובנת מאליה לקוראים רבים.
חוץ מהערות תיעוד רצוי לתת למשתנים שמות שיש להם קשר לערכים המוצבים בהם וכדאי לרווח את הקוד באמצעות תווי רווח ושורות רווח. גם אלה מקלים על הבנת התכנית.
(11) סיכום
נעיין בבעיית מבצע השוקולד:
מאז ומתמיד שאפה מטילדה דנטון לטעום מטעמם הערב של השוקולדים הנמכרים בבוטיק השוקולדים “מריר”. למזלה הרב לפני שבועיים פרסמה החנות הודעה כי ביום מבצע מיוחד היא תאפשר לכל לקוחה ולקוח לטעום קוביה אחת מכל אחד ואחד מ-3 סוגי השוקולדים בחנות, וכי כל קוביה תימכר במחיר מופחת מהרגיל. בנוסף תווי קנייה בשווי אקראי בין 100 ל-200 ש”ח יוענקו ל-5 לקוחות. אתמול נערך יום המבצע המיוחד. מטילדה הייתה אחת מהלקוחות שנהנו ממנו, ומזלה היה אף רב מזה: היא זכתה בתווי קנייה. כתבו תכנית המקבלת את המחיר של כל אחת מ-3 קוביות השוקולד שאכלה (המחיר אינו אחיד), ואת השווי הכולל של תווי הקניה שזכתה בהם, מחשבת את ההפרש בין התשלום הכולל בעד הקוביות לשווי הכולל של תווי הקניה, ומודיעה על גובה ההפרש.
באמצעות הידע שקניתם בפרק זה יש בכוחכם לכתוב אלגוריתם הפותר בעיה זו ולממש את האלגוריתם בתכנית פייתון. הידע מקיף מושגים יסודיים בכתיבת תכנית פייתון ובהם ערכים, מספרים, מחרוזות, פונקציות, מודולים, והערות תיעוד. הוא מאפשר לכם גם להתמודד היטב מול שגיאות היכולות להיווצר בכתיבת התכנית. נדבך עיקרי של הלימוד בהמשך הקורס יהיה העשרת ‘ארגז הכלים’ של השפה, בפונקציות ובכלים אחרים שהיא מעמידה לרשותנו. כך נעשה כבר בפרק הבא, שבו נעמיק את הדיון בזרימה של תכנית פייתון.