Smart-World Surf

יחידה 3: פונקציות ומודולריות

ארגון קוד לפונקציות לשימוש חוזר ופישוט.

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

פונקציות: אבני הבניין של הקוד

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

פונקציה (Function): בלוק של קוד המבצע משימה ספציפית, וניתן לקרוא לו (להפעיל אותו) מספר פעמים מנקודות שונות בתוכנית.
פרמטר (Parameter): משתנה המוגדר בכותרת הפונקציה ומקבל ערך כאשר הפונקציה נקראת. הוא מאפשר לפונקציה להיות גמישה ולפעול על נתונים שונים.
ערך מוחזר (Return Value): הערך שהפונקציה מחזירה למקום שממנו היא נקראה, באמצעות הפקודה return. פונקציה יכולה להחזיר כל סוג של נתון.

הגדרת וקריאת פונקציות ב-Python

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

def greet(name):
    message = "שלום, " + name + "!"
    return message

# קריאה לפונקציה
my_greeting = greet("עולם")
print(my_greeting) # פלט: שלום, עולם!

מודולריות ויתרונותיה

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

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

יתרונות המודולריות

שימוש חוזר (Reusability)

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

פישוט ובהירות (Simplification & Clarity)

פירוק בעיה מורכבת לתתי-בעיות קטנות יותר, שכל אחת מטופלת על ידי פונקציה, מקל על הבנת הקוד ותחזוקתו.

קלות בדיקה ותיקון (Easier Testing & Debugging)

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

שיתוף פעולה (Collaboration)

צוותי פיתוח יכולים לעבוד במקביל על פונקציות שונות, תוך שילוב קל יחסית של העבודה בסוף הפרויקט.

טווח משתנים (Scope) - נושא קריטי למבחן

טווח משתנים (Scope): הבנה מעמיקה של טווח משתנים (מקומי וגלובלי) היא קריטית להצלחה במבחן. שאלות רבות בבחינות בודקות את היכולת לעקוב אחר ערכי משתנים בתוך ומחוץ לפונקציות, במיוחד במקרים של שינוי משתנים גלובליים מתוך פונקציות. זכרו את הכלל: פונקציה יכולה לקרוא ממשתנה גלובלי, אך כדי לשנות אותו עליה להצהיר עליו במפורש כ-global.
משתנה מקומי (Local Variable): משתנה המוגדר בתוך פונקציה וקיים רק בתוך אותה פונקציה. הוא נוצר עם כניסה לפונקציה ונעלם עם יציאה ממנה.
משתנה גלובלי (Global Variable): משתנה המוגדר מחוץ לכל פונקציה ונגיש מכל מקום בתוכנית.

דוגמה לטווח משתנים

x = 10 # משתנה גלובלי

def my_function():
    y = 5 # משתנה מקומי
    print(f"בתוך הפונקציה: x={x}, y={y}") # יכולה לקרוא ל-x הגלובלי

def another_function():
    global x # מצהיר ש-x מתייחס למשתנה הגלובלי
    x = 20 # משנה את המשתנה הגלובלי
    z = 3 # משתנה מקומי
    print(f"בתוך פונקציה אחרת: x={x}, z={z}")

my_function() # פלט: בתוך הפונקציה: x=10, y=5
print(f"מחוץ לפונקציה: x={x}") # פלט: מחוץ לפונקציה: x=10

another_function() # פלט: בתוך פונקציה אחרת: x=20, z=3
print(f"מחוץ לפונקציה אחרי קריאה ל-another_function: x={x}") # פלט: מחוץ לפונקציה אחרי קריאה ל-another_function: x=20
# print(y) # שגיאה! y הוא משתנה מקומי ל-my_function

תרחישי שימוש נפוצים ודוגמאות

אחת המטרות העיקריות של פונקציות היא לפרק בעיה גדולה לתתי-בעיות קטנות יותר. לדוגמה, אם נדרש לנתח נתונים מורכבים (כמו קובץ DNA או מטריצה), נוכל לכתוב פונקציות נפרדות לכל שלב.

דוגמה: פונקציות לעיבוד מטריצות

בהתבסס על תרגילי הבית, סביר להניח שתרגלתם פונקציות המטפלות במטריצות (רשימות מקוננות). הנה דוגמה לפירוק פעולות מטריצה לפונקציות:

def create_matrix(rows, cols, default_val=0):
    """יוצרת מטריצה בגודל נתון עם ערך ברירת מחדל."""
    matrix = []
    for _ in range(rows):
        matrix.append([default_val] * cols)
    return matrix

def add_matrices(matrix1, matrix2):
    """מחברת שתי מטריצות בגודל זהה."""
    if len(matrix1) != len(matrix2) or len(matrix1[0]) != len(matrix2[0]):
        return None # או לזרוק שגיאה
    
    rows = len(matrix1)
    cols = len(matrix1[0])
    result_matrix = create_matrix(rows, cols)
    
    for i in range(rows):
        for j in range(cols):
            result_matrix[i][j] = matrix1[i][j] + matrix2[i][j]
    return result_matrix

# שימוש בפונקציות
mat_a = create_matrix(2, 3, 1)
mat_b = create_matrix(2, 3, 2)
mat_c = add_matrices(mat_a, mat_b)
# print_matrix(mat_c) - פונקציה נוספת להדפסה

טעויות נפוצות ואתגרי תכנון

למרות שפונקציות הן כלי חזק, שימוש יתר או שימוש שגוי עלול להזיק:

  • פונקציות קטנות מדי (Over-modularization): פונקציה שמבצעת פעולה בודדת ופשוטה מאוד, ולא נדרשת לשימוש חוזר, עלולה להקשות על קריאות הקוד במקום לפשט אותו.
  • תלות יתר במשתנים גלובליים: פונקציות המסתמכות יתר על המידה על שינוי משתנים גלובליים הופכות את הקוד לקשה יותר למעקב ולבדיקה. עדיף להעביר נתונים כפרמטרים ולהח
    מצאתם טעות או שחסר משהו?
→ הקודמת
בקרת זרימה ולולאות
הבאה ←
מבני נתונים יסודיים: רשימות ומחרוזות