פונקציה היא קטע קוד קצר ותחום שנועד לפתור בעיה ספציפית.
כל פונקציה היא מעין רכיב בתוכנה שלנו שנושא שם מסוים. אפשר להסתכל על פונקציה כקטע קוד שמממש משימה מוגדרת היטב.
לדוגמה, פונקציה יכולה להיות "המר טמפרטורה ממעלות פרנהייט לצלזיוס".
במובן הזה, פונקציות הן כמו תוכניות קטנות – יש להן קלט, הן עושות תהליך מסוים ומחזירות תוצאה מסוימת.
אם ניקח את הפונקציה המדוברת מלמעלה, אפשר לתאר אותה כתוכנית קטנה כך:
פונקציות הן למעשה אבני הבניין של תוכנה – כל פונקציה שואפת למלא תפקיד מוגדר בתוכנה, ובעזרת הרבה אבני בניין שכאלו אפשר לבנות תוכנה מורכבת.
התוכנות שבהן אתם משתמשים ביום־יום מורכבות בדרך כלל מכמות רבה מאוד של פונקציות.
עד סוף השיעור נדע איך יוצרים פונקציה כזו ואיך משתמשים בה.
נדמיין פונקציה כמכונה שמקבלת ערכים, ויודעת לתת לנו פלט מסוים לפי אותם ערכים.
אחרי שכתבנו פונקציה ובדקנו שהיא עובדת, נוכל להשתמש בה בלי לנסות לפענח בכל פעם מחדש את הלוגיקה שנמצאת בתוכה.
אחת הפונקציות שכמעט כולם מכירים היא העלאה בריבוע.
הקלט של הפונקציה הוא מספר כלשהו, התהליך שקורה בפונקציה הוא הכפלת המספר בעצמו, והפלט הוא תוצאת המכפלה.
לדוגמה, הקלט של פונקציה בשם "העלאה בריבוע" יכול להיות 5, והפלט שלה יהיה 25.
דוגמאות נוספות לקלט הן -3, שעבורו הפלט יהיה 9,
או 0.5, שעבורו הפלט יהיה 0.25.
דרך קלה לקבל הערכה גסה של כמה קלוריות יש במוצר מזון, היא זו:
כתבו תוכנית (ללא פונקציות) המקבלת מהמשתמש את ערכי הפחמימות, השומן והחלבון במוצר.
הדפיסו למשתמש את כמות הקלוריות במוצר.
פתרו את התרגיל לפני שתמשיכו הלאה.
חשוב!
פתרו לפני שתמשיכו!
והנה הפתרון לשאלה מלמעלה (אזהרת ספוילר!)
In [ ]:
carbs = int(input("How many carbohydrates does this product have?: "))
fat = int(input("How much fat does this product have?: "))
protein = int(input("How much protein does this product have?: "))
total_calories = carbs * 4 + fat * 9 + protein * 4
print("This product has " + str(total_calories) + " calories in it.")
ההתחלה תהיה קפיצת ראש למים העמוקים, אבל אל חשש! אני אסביר בקרוב מה קורה כאן. בינתיים אני בונה קצת מתח.
דבר ראשון נזרוק את הפתרון של התרגיל בתוך פונקציה.
In [ ]:
def calculate_calories():
carbs = int(input("How many carbohydrates does this product have?: "))
fat = int(input("How much fat does this product have?: "))
protein = int(input("How much protein does this product have?: "))
calories = carbs * 4 + fat * 9 + protein * 4
print("This product has " + str(calories) + " calories in it.")
בתא שממש פה מעל, השתמשנו במילת המפתח def ובעזרתה הגדרנו פונקציה שנקראת calculate_calories.
לפני שניכנס לפרטים על אודות ההגדרה ומה היא עושה, הריצו את התא ונסו לשים אצבע על תופעה משונה בתא הזה...
ובכן, התופעה המשונה היא שבניגוד לפתרון התרגיל בלי הפונקציה שכתבנו למעלה, התא שבו הפונקציה כתובה לא עושה כלום!
ובכן, זה מאחר שפונקציות לא פועלות מייד אחרי שמגדירים אותן.
בניגוד למסרונים בתקופת בחירות, לפונקציות צריך ממש לקרוא אקטיבית כדי שיבואו.
נעשה את זה, לפחות בינתיים, על ידי כתיבת השם של הפונקציה ואז סוגריים עגולים ריקים:
In [ ]:
calculate_calories()
מה שעשינו בתא שמעל נקרא קריאה לפונקציה, וזו למעשה הדרך לגרום לתוכן של הפונקציה לרוץ.
זה ממש כמו להפעיל מעין תוכנה קטנה בתוך התוכנה שלנו.
מזל טוב! יש לנו פונקציה עובדת.
זה אפילו היה די קל:
def function_name():, כש־function_name הוא בעצם השם של הפונקציה.
קריאה לפונקציה חייבת להופיע אחרי הגדרתה.
אם קודם נקרא לפונקציה ורק אז נגדיר אותה, פייתון עדיין לא תכיר את הפונקציה ותזרוק לנו שגיאה.
כדי לזקק את הרעיון, ננסה להגדיר פונקציה פשוטה יחסית, ונקרא לה מייד כדי להפעיל אותה.
שימו לב למינוחים החשובים: כותרת הפונקציה, גוף הפונקציה, שם הפונקציה והקריאה לפונקציה.
In [ ]:
# שם הפונקציה
def hello_world(): # Function Header / כותרת הפונקציה
print("Hello World!") # גוף הפונקציה (מוזח!)
hello_world() # הקריאה לפונקציה
נהוג להגיד שכל פונקציה צריכה להיות אחראית לדבר אחד בלבד.
במילים אחרות – כמה שפחות עבודה בתוך כל פונקציה, וכמה שיותר התמקדות במה שהיא באמת צריכה לעשות, כך ייטב.
אחד הדברים הראשונים שנוח לנו להוציא מהפונקציה שלנו זה הקלט מהמשתמש, שביצענו בתוך הפונקציה:
In [ ]:
def calculate_calories():
calories = carbs * 4 + fat * 9 + protein * 4
print("This product has " + str(calories) + " calories in it.")
carbs = int(input("How many carbohydrates does this product have?: "))
fat = int(input("How much fat does this product have?: "))
protein = int(input("How much protein does this product have?: "))
calculate_calories()
הקוד הזה אומנם יעבוד, אבל הוא כתוב בצורה גרועה מאוד.
הוא מפר את העיקרון שהתחלנו איתו – פונקציה אמורה להיות יחידה עצמאית, קטנה וארוזה.
היא אמורה להיות תלויה כמה שפחות בסביבה החיצונית.
בתוך הפונקציה אנחנו מתייחסים למשתנים fat, protein ו־carbs, שאינם חלק מהפונקציה.
ייתכן שבעתיד מישהו יעתיק את הפונקציה calculate_calories לתוכנית שלו, והמשתנים הללו לא יהיו מוגדרים בגוף התוכנית.
במקרה כזה, לפונקציה לא יהיה מאיפה לקחת את הערכים והיא תקרוס.
הדרך להתמודד עם הבעיה הזו היא לחייב את הפונקציה לקבל באופן מסודר את הנתונים האלו מבחוץ.
נראה דוגמה:
In [ ]:
def calculate_calories(carbs, fat, protein):
calories = carbs * 4 + fat * 9 + protein * 4
print("This product has " + str(calories) + " calories in it.")
שימו לב לכותרת הפונקציה.
הכנסנו לשם את שמות 3 המשתנים הדרושים להרצת הפונקציה.
אם תרצו, תוכלו להסתכל על זה כ"הקלט שנדרש עבור הפונקציה", או במקרה שלנו: "הנתונים שדרושים כדי לחשב את כמות הקלוריות במוצר".
החלק הזה שהוספנו לפונקציה נקרא הפרמטרים של הפונקציה: כל אחד מהנתונים carbs, fat ו־protein הוא פרמטר.
השם התיאורטי והמפונפן שמתכנתים משתמשים בו עבור השילוב של שם הפונקציה עם שמות הפרמטרים שלה נקרא חתימת הפונקציה.
איך תראה הקריאה לפונקציה עכשיו?
אותו דבר, רק שבתוך הסוגריים נכתוב את הערכים שעבורם נרצה לחשב את כמות הקלוריות, לפי הסדר של הפרמטרים.
הערכים שנעביר בתוך הקריאה לפונקציה נקראים ארגומנטים.
In [ ]:
calculate_calories(50, 90, 20)
אם כך, בארוחה שבה 50 גרם פחמימות, 90 גרם שומן ו־20 גרם חלבון, יש כ־1,090 קלוריות.
נסכם את מה שעשינו עד עכשיו ונרענן את הטרמינולוגיה שלנו:
In [ ]:
# שם הפונקציה
# פרמטרים
def calculate_calories(carbs, fat, protein): # כותרת הפונקציה
calories = carbs * 4 + fat * 9 + protein * 4
print("This product has " + str(calories) + " calories in it.")
product_carbs = int(input("How many carbohydrates does this product have?: "))
product_fat = int(input("How much fat does this product have?: "))
product_protein = int(input("How much protein does this product have?: "))
# ארגומנטים
calculate_calories(product_carbs, product_fat, product_protein)
מותר שבתוך פונקציה יוגדר משתנה ששמו זהה למשתנה שנמצא מחוץ לפונקציה.
מתכנתים רבים מעדיפים להימנע מכך כדי למנוע באגים.
הפונקציה שלנו מתחילה להיראות נהדר, אבל מתכנתים מנוסים עדיין יעקמו את האף.
כמו שאמרנו, פונקציה אמורה לעשות כמה שפחות, ולכן נעדיף שהיא גם לא תדפיס בעצמה את הערך שחישבה.
היינו מעדיפים לקבל מהפונקציה רק את מספר הקלוריות שחישבנו, בלי טקסט מיותר.
יש לצורת עבודה כזו יתרונות משמעותיים.
אם הפונקציה תחזיר ערך מספרי, נוכל לעשות בו שימוש בפונקציות אחרות כדי לבצע פעולות המשך על התוצאה, כמו נניח:
אוקיי, על פניו יש דרך פשוטה למדי לטפל בזה.
בואו ננסה להפעיל את הפונקציה, ואז ניגש לערך calories שנמצא בתוכה!
In [ ]:
def calculate_calories(carbs, fat, protein):
calories = carbs * 4 + fat * 9 + protein * 4
calculate_calories(5, 30, 5)
print(calories)
מממ... זה לא עבד כמצופה.
הסיבה שלא הצלחנו לקבל את התוכן של המשתנה calories מתוך הפונקציה, היא בגלל העיקרון שפונקציה היא קופסה סגורה.
לכן, משתנים שמוגדרים בתוך הפונקציה זמינים אך ורק בתוכה, ונעלמים כשהיא מסיימת לרוץ.
אז איך בכל זאת אפשר לקבל את תוצאת החישוב שלנו מתוך פונקציה?
הביטו על הפונקציה הבאה:
In [ ]:
def calculate_calories(carbs, fat, protein):
calories = carbs * 4 + fat * 9 + protein * 4
return calories
עוד לפני שאתייחס למילת המפתח החדשה return, שימו לב כמה נעים לקרוא את הפונקציה הזו.
הפונקציה מתמקדת אך ורק במה שהיא אמורה לעשות, ואפשר להבין בקלות מה המטרה שלה ומה היא מבצעת.
גם מתכנת שכלל לא מכיר את המטרה שלנו ואת התרגיל שממנו התחלנו, יכול במבט חטוף להבין מה ניסינו לעשות.
ננסה לקרוא לפונקציה החדשה שהגדרנו, ונראה מה יקרה:
In [ ]:
calculate_calories(50, 17.5, 36)
מעניין! הפונקציה ממש החזירה לנו ערך.
זה אומר שהצלחנו לקבל מתוך הפונקציה נתון ממשי שאפשר לעבוד איתו.
בואו נשחק עם זה:
In [ ]:
print("Calories in a Hamburger: " + str(calculate_calories(50, 17.5, 36)))
print("Calories in 2 Hamburgers: " + str(2 * calculate_calories(50, 17.5, 36)))
אם זה עוזר לתפיסת הנושא עבורכם, תוכלו לדמיין מה קורה מאחורי הקלעים:
פייתון רואה קריאה לפונקציה. היא מעבירה את הארגומנטים ששלחתם לתוך הפרמטרים שמופיעים בכותרת הפונקציה.
כשהפונקציה מחזירה ערך, החלק בקוד שקרא לפונקציה ממש מתחלף בערך שחזר מהפונקציה.
כך ששלב הביניים, לפני ההדפסה למסך, נראה מבחינת פייתון כך (השוו עם המשבצת הקודמת):
In [ ]:
print("Calories in a Hamburger: " + str(501.5))
print("Calories in 2 Hamburgers: " + str(2 * 501.5))
נוכל להשתמש ברעיון הזה כדי לכתוב את הקוד בצורה קריאה יותר – נשמור את הערך שהפונקציה החזירה לנו בצד, ונשתמש בו:
In [ ]:
calories_in_hamburger = calculate_calories(50, 17.5, 36)
print("Calories in a Hamburger: " + str(calories_in_hamburger))
print("Calories in 2 Hamburgers: " + str(2 * calories_in_hamburger))
זה הזמן לראות את התוכנית שלנו מוכנה, עם כל הטרמינולוגיה עליה:
In [ ]:
# שם הפונקציה
# פרמטרים
def calculate_calories(carbs, fat, protein): # כותרת הפונקציה
calories = carbs * 4 + fat * 9 + protein * 4
return calories # ערך ההחזרה
product_carbs = int(input("How many carbohydrates does this product have?: "))
product_fat = int(input("How much fat does this product have?: "))
product_protein = int(input("How much protein does this product have?: "))
# ארגומנטים
cal = calculate_calories(product_carbs, product_fat, product_protein)
print("This product has " + str(cal) + " calories in it.")
העיקרון האחרון שניגע בו הוא העיקרון של החזרה מיידית.
ברגע שפונקציה מגיעה לשורה שבה כתוב return, היא תחזיר ערך ותסיים את ריצתה באותה שורה.
בואו נראה דוגמה:
In [ ]:
def give_me_numbers(number):
return number + 1
return number + 2
return number + 3
print(give_me_numbers(5))
במקרה הזה, הערך היחיד שחזר הוא 6.
הסיבה לכך היא שאחרי השורה return number + 1, הפונקציה החזירה ערך והפסיקה לרוץ.
לפניכם דוגמה של פונקציה שמחשבת את הערך המוחלט של מספר.
לצורך הדוגמה הזו, הערך המוחלט של מספר מוגדר כמרחק שלו מהמספר אפס.
למשל, הערך המוחלט של 6 הוא 6, והערך המוחלט של -4 הוא 4.
In [ ]:
def absolute_value(number):
if number < 0:
number = number * -1
return number
print(absolute_value(-5))
תרגול:
כתבו פונקציה שמקבלת שני מספרים ומחזירה את הגדול מביניהם.
רוצים אתגר? פתרו את התרגיל ב־4 שורות.
ייתכן שלא שמתם לב, אבל אתם כבר מכירים פונקציות מועילות ואפילו יצא לכם להשתמש בהן!
לדוגמה, הפונקציה print שמטרתה להדפיס למסך הודעה שהיא מקבלת כארגומנט,
או הפונקציה type שמטרתה להחזיר את סוג המשתנה שהיא מקבלת כארגומנט.
עכשיו, כשאתם כבר מבינים איך עובדות פונקציות, הגיע הזמן להכיר פונקציה חדשה: len.
הפונקציה הזו מקבלת מחרוזת – ומחזירה את האורך שלה.
בואו ננסה:
In [ ]:
print(len('hello world'))
In [ ]:
# פתרו לפני שתריצו: מה יהיה הערך שיודפס? מה ההבדל בין האגפים?
len('hello world') * 2 == len('hello world' * 2)
פונקציות מחלקות את הקוד שלנו לקטעים קטנים ובעלי שם, שיש מאחוריהם רעיון אחד שקל לקרוא ולתפוס.
חלוקה לקטעים קטנים מאפשרת לנו לעקוב ביעילות אחרי מבנה התוכנית.
כשנרצה לערוך חלק מסוים בתוכנית בשלב כלשהו – נדע בקלות ובמהירות איזה קטע בקוד צריך לשנות.
פונקציות עוזרות לנו למנוע מצבים שבהם היינו צריכים לכתוב קוד כמה פעמים.
זה מועיל מכיוון שזה מאפשר לנו להשתמש מאוד מהר בקוד שכבר כתבנו בעבר.
סיבה נוספת שיש להזכיר לחיוב, היא שתחזוקת הקוד קלה במידה ניכרת כשמבצעים שינוי במקום אחד מוגדר, במקום במקומות רבים ברחבי הקוד.
def, וכוללת את שם הפונקציה ואת שמות הפרמטרים שלה.
פונקציה היא מעין יחידת תוכנה בסיסית וקטנה.
היא עוזרת לנו לחלק את התוכנה שלנו לתתי־משימות, ועושה אותה לקריאה יותר ולקלה יותר לתחזוקה.
את הפונקציה אנחנו מגדירים בעזרת כותרת הפונקציה, שמכילה את מילת המפתח def, ואז את שם הפונקציה ואת הפרמטרים שהיא אמורה לקבל.
מייד לאחר כותרת הפונקציה נגדיר בהזחה את גוף הפונקציה – שכולל את החישובים או את הפעולות שהפונקציה צריכה לבצע,
ובסוף נבצע החזרה לערך ההחזרה שיופיע אחרי מילת המפתח return.
כדי להשתמש בפונקציה נקרא לפונקציה בהמשך הקוד על ידי כתיבת השם שלה, ובתוך סוגריים נפרט את הארגומנטים, שהם הערכים שנרצה להעביר לפונקציה.
חשוב לזכור!
המשתנים בתוך הפונקציה אינם נגישים מבחוץ, כיוון שהיא נחשבת יחידה סגורה.
כמו כן, ברגע שהפונקציה מריצה שורה שבה כתוב return, היא מחזירה ערך וריצתה נפסקת.
דני מתאמן למרתון הקרוב. הוא מתחיל להתכונן על אש נמוכה, ורץ אתמול 5 קילומטרים.
כתבו פונקציה שמקבלת כמה שעות לקח לדני לגמוע את 5 הקילומטרים הללו, ומחשבת מה קצב הריצה הממוצע של דני, בקמ"ש.
כתבו פונקציה נוספת שבודקת אם דני יכול להשלים מרתון בתוך 3 שעות.
אורכו של מסלול מרתון הוא 42.195 קילומטר.
ניתן לחשב באיזו מהירות (בקמ"ש) דני רץ אם ניקח את כמות הקילומטרים שעבר, ונחלק אותה בכמות השעות שרץ.
ניתן לחשב כמה זמן יקח לדני לרוץ מסלול מסוים, אם ניקח את המרחק בקילומטרים שרץ ונחלק אותו במהירות של דני (בקמ"ש).
ניתן לחשב כמה קילומטרים דני ירוץ בזמן נתון, אם ניקח את המהירות שלו בקמ"ש ונכפיל אותה בזמן שלקח לו לרוץ את המרחק הזה.
הפתרון מופיע בתחתית המחברת.
כתבו פונקציה שמקבלת 3 מחרוזות כארגומנטים ומחזירה את המחרוזת הארוכה יותר מביניהן.
זוהי גרסה של תרגיל פופולרי בראיונות עבודה.
כתבו פונקציה שמקבלת מספר.
אם המספר מתחלק ב־3 ללא שארית, הדפיסו Fizz.
אם המספר מתחלק ב־5 ללא שארית, הדפיסו Buzz.
אם המספר מתחלק גם ב־3 וגם ב־5 ללא שארית, הדפיסו FizzBuzz.
אם המספר לא מתחלק ב־3 ולא מתחלק ב־5, הדפיסו את המספר עצמו.
לדוגמה,
עבור המספר 9 הדפיסו Fizz,
עבור המספר 10 הדפיסו Buzz,
עבור המספר 15 הדפיסו FizzBuzz,
ועבור המספר 7 הדפיסו 7.
כתבו פונקציה שמקבלת מספר דו־ספרתי שספרת האחדות שלו שונה מ־0.
הפונקציה תחזיר את המספר הפוך.
לדוגמה: עבור 53 הפונקציה תחזיר 35. עבור 19 הפונקציה תחזיר 91. עבור 91 הפונקציה תחזיר 19.
זהו תרגיל ברמת קושי גבוהה, שמערב נושאים רבים.
הרגישו בנוח להיעזר במתרגלים שלכם.
הצופן לכספת הביתית שלכם הוא 4812. בנו משחק בול־פגיעה שמאפשר למשתמש לנסות לנחש את הצופן.
למשתמש יש 3 ניסיונות לנחש נכונה את הצופן שלכם לפני שמופעלת אזעקה.
כחלק ממנגנון ההגנה מאיבוד הסיסמה של הכספת, היא מציגה כמה ספרות נכונות המשתמש הזין אחרי כל ניחוש.
אפשרו למשתמש להזין קוד 3 פעמים, וכתבו לו בכל ניסיון כמה מתוך הספרות שהזין באמת קיימות בקוד הנכון, לאו דווקא בסדר שהקיש.
אם לא הצליח אחרי 3 ניסיונות, הדפיסו שהאזעקה הופעלה וסיימו את התוכנית.
לדוגמה, אם המשתמש הקיש בניסיון הראשון 0634, הדפיסו לו שרק אחת הספרות שניחש נכונה.
אם המשתמש הקיש בסיבוב השני 1234, הדפיסו לו ש־3 ספרות תואמות את הקוד המקורי.
אם המשתמש הקיש בסיבוב השלישי 1284, הדפיסו לו ש־4 ספרות תואמות את הקוד המקורי, ואז הדפיסו לו שהופעלה האזעקה.
אם המשתמש הקיש באחד הסיבובים 4812, הדפיסו שהכספת נפתחה בהצלחה וסיימו את התוכנית מייד.
קבלו מספר וחשבו את העצרת שלו – מכפלת כל המספרים עד אותו מספר.
לדוגמה, 5 עצרת שווה ל־1 כפול 2 כפול 3 כפול 4 כפול 5, והתוצאה היא 120.
זהירות! זה תרגיל בונוס, והוא קשה מאוד. מאוד. מאוד מאוד. אנחנו לא אומרים סתם. הוא לא חובה ומיועד לאנשים שרוצים לאתגר את עצמם. מאוד.
In [ ]:
def calculate_km_per_hour_for_5_km(hours_to_finish):
km_per_hour = 5 / hours_to_finish
return km_per_hour
def check_if_run_marathon_in_three_hours(km_per_hour):
hours_to_finish_marathon = 42.195 / km_per_hour
return hours_to_finish_marathon <= 3
time = float(input("How many hours did it take to finish a 5km run? "))
km_per_hour = calculate_km_per_hour_for_5_km(time)
can_run_marathon_in_three_hours = check_if_run_marathon_in_three_hours(km_per_hour)
if can_run_marathon_in_three_hours:
print("Yes you can!")
else:
print("No you can't!")
בואו ננקה, נסדר ונהפוך דברים ליותר כלליים:
In [ ]:
MARATHON_DISTANCE = 42.195
def calculate_km_per_hour(km_to_run, hours_took_to_finish):
km_per_hour = km_to_run / hours_took_to_finish
return km_per_hour
def calculate_time_to_run(distance_to_run, km_per_hour):
hours = distance_to_run / km_per_hour
return hours
def print_if_can_run(target_time, actual_time):
# זה בסדר להדפיס פה, כי זה ממש המטרה של הפונקציה.
if actual_time <= target_time:
print("Success! You can do it!")
else:
print("You can't do it :(")
time = float(input("How many hours did it take to finish a 5km run? "))
km_per_hour = calculate_km_per_hour(5, time)
hours_to_finish = calculate_time_to_run(MARATHON_DISTANCE, km_per_hour)
print_if_can_run(3, hours_to_finish)