יסודות התכנות בשפת Python · פרק 7 — פעולות (פונקציות)⌂📚🏠
פרק 7

פעולות (פונקציות)

20 שעות לימוד

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

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

7.1 פעולות ככלי לעידון וחלוקת משימות

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

v = 12
r = 480
print("V =", v, " R =", r)
print("I =", v / r, "A")
v = 5
r = 250
print("V =", v, " R =", r)
print("I =", v / r, "A")
פלט
V = 12  R = 480
I = 0.025 A
V = 5  R = 250
I = 0.02 A

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

פעולה: קטע קוד שיש לו שם ותפקיד ברור. כותבים אותו פעם אחת, ומפעילים אותו באמצעות שמו בכל פעם שצריך. היא נקראת גם פונקציה, אבל בבחינה השם הוא פעולה.
def show_current(v, r):
    print("V =", v, " R =", r)
    print("I =", v / r, "A")

show_current(12, 480)
show_current(5, 250)
פלט
V = 12  R = 480
I = 0.025 A
V = 5  R = 250
I = 0.02 A

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

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

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

את חישוב הסטייה נכתוב בסעיף 7.4, את הפרדת הנפסלים בסעיף 7.8, ואת התוכנית כולה נרכיב בסעיף 7.11.

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

7.2 הגדרת תפקיד הפעולות

לפני שכותבים את הגוף מגדירים את תפקיד הפעולה, בתשובה על ארבע שאלות: מה שמה? מה היא מקבלת? מה היא נותנת? ומה היא עושה, במשפט אחד? ננסה זאת על פעולות מוכרות, ובשורה האחרונה על פעולה שעוד נכתוב:

הפעולהמקבלתנותנתתפקידה
len(s)מחרוזת או רשימהמחזירה מספר שלםמספר התווים או האיברים
print(a, b)ערכים מכל טיפוסמדפיסה ואינה מחזירה דברהצגת הערכים על המסך
deviation(n, m)שני מספריםמחזירה מספרסטיית המדידה מן הערך הנקוב באחוזים

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

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

print("start")

def beep():
    print("beep")

beep()
print("end")
beep()
פלט
start
beep
end
beep
הערה: בבחינה השאלה מגיעה בכיוון ההפוך: נתונה פעולה, ומבקשים לכתוב "במשפט אחד" מה היא עושה. קודם מנסים אותה על דוגמה או שתיים, ואז מתארים את התוצאה ולא את השורות. לא כותבים "עוברת על הרשימה ומשווה כל איבר לקודמו", אלא "מחזירה את מספר הקריאות הגבוהות מקודמתן".
טעות נפוצה: פעולה שנקראת בשם של פעולה מוכנה מסתירה אותה: אחרי def max(a, b): אי אפשר להשתמש ב-max המוכנה בכל התוכנית. לכן בוחרים שם אחר.
שאלה 7.2: מה מדפיסה התוכנית? כתבו במשפט אחד את תפקיד הפעולה mystery.
def mystery(bits, n):
    k = 0
    for b in bits:
        if b == 1:
            k += 1
    print(k >= n)

mystery([1, 0, 1, 1], 3)
mystery([1, 0, 0, 1], 3)

7.3 פעולות המקבלות פרמטרים, כולל פרמטרים עם ערך ברירת מחדל

פרמטר: משתנה שנכתב בסוגרי ההגדרה ומקבל את ערכו מן הזימון. גוף הפעולה עובד איתו כמו עם כל משתנה. הוא מקבל ערך מכל טיפוס: מספר, מחרוזת, ערך בוליאני, או רשימה כמו בסעיף 7.8.
טעות נפוצה: בזימון מציבים ערך לכל פרמטר: מספר, משתנה, או ביטוי שמחושב קודם. אם חסר ערך, התוכנית נעצרת בהודעה שמציינת את הפרמטר החסר. אבל הטעות המסוכנת יותר היא הסדר, כי הערכים מתחלקים בין הפרמטרים לפי סדרם. בזימון השני הגיעו שני הערכים בסדר שגוי, ולכן המתח נהיה 480 וההתנגדות 12, ולא הופיעה שום הודעה. אפשר לציין את שם הפרמטר בזימון, כמו בזימון השלישי, ואז הסדר אינו חשוב:
def show_current(v, r):
    print("I =", round(v / r, 3), "A")

show_current(12, 480)
show_current(480, 12)
show_current(r=480, v=12)
פלט
I = 0.025 A
I = 40.0 A
I = 0.025 A
ערך ברירת מחדל: ערך שכותבים בהגדרה אחרי הסימן =. אם הפרמטר לא צוין בזימון הוא מקבל אותו, ואם צוין, הערך החדש בא במקומו. אנחנו כבר מכירים זאת: print מדפיסה עם רווח מפריד כל עוד לא קבענו sep.
דוגמה 7.2
מחברים נורית למקור מתח דרך נגד שקובע את הזרם שלה. רוב הנוריות פועלות במתח 2 V ובזרם 20 mA, ולכן נקבע אותם כערכי ברירת מחדל, ונציין אותם רק כשהם שונים.
פתרון:
R = VS − VF I
def led_series(vs, vf=2.0, i=0.02):
    r = (vs - vf) / i
    print("R =", round(r), "ohm")

led_series(12)
led_series(5, 3.2)
led_series(5, 3.2, 0.01)
led_series(5, i=0.01)
פלט
R = 500 ohm
R = 90 ohm
R = 180 ohm
R = 300 ohm

בזימון האחרון דילגנו על vf בכך שציינו את השם i.

טעות נפוצה: פרמטרים עם ערך ברירת מחדל באים אחרי הפרמטרים שאין להם ערך כזה. לכן ההגדרה def led_series(vf=2.0, vs): היא שגיאת תחביר: פייתון לא תדע למי לתת את הערך היחיד ב-led_series(12).
שאלה 7.3: מה מדפיסה התוכנית הבאה?
def show(x, k=2, d=0):
    print(x * k + d)

show(3)
show(3, 4)
show(3, d=1)
show(3, 4, 1)

7.4 פעולות המחזירות ערך

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

נכתוב את חוק אום כפעולה שמחזירה את הזרם. הזימון שווה לערך המוחזר, ולכן אפשר להציב אותו במשתנה, לכתוב אותו בתוך ביטוי או להעביר אותו ל-print:

def current(v, r):
    return v / r

i = current(12, 480)
print(i)
print(current(9, 300) * 1000, "mA")
print(current(5, 250) + current(12, 400))
פלט
0.025
30.0 mA
0.05
טעות נפוצה: כותבים print במקום return, רואים את המספר על המסך וחושבים שהפעולה עובדת. אבל היא אינה מחזירה דבר, ולכן i מקבל את הערך None, והתוכנית נעצרת בחישוב הראשון. print מיועדת לאדם שמסתכל על המסך, ו-return לשאר התוכנית:
def current(v, r):
    print(v / r)

i = current(12, 480)
print(i * 1000)
פלט
0.025
TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'

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

דוגמה 7.3
נחזור לארגז הנגדים מסעיף 7.1. נכתוב פעולה שרק מחשבת את הסטייה, כלומר את ההפרש המוחלט בין המדידה לערך הנקוב כאחוז מן הערך הנקוב, ופעולה שנייה ששואלת אם הרכיב מתקבל. סבילות של 5 אחוזים היא ערך ברירת המחדל.
פתרון:
def deviation(nominal, measured):
    """Deviation from the nominal value, in percent."""
    return abs(measured - nominal) * 100 / nominal

def is_ok(nominal, measured, tol=5):
    return deviation(nominal, measured) <= tol

print(deviation(1000, 1030))
print(is_ok(1000, 1030))
print(is_ok(1000, 1080))
print(is_ok(1000, 1080, 10))
if not is_ok(470, 500):
    print("Reject")
פלט
3.0
True
False
True
Reject

הפעולה is_ok מחזירה True או False, ולכן כותבים את הזימון שלה ישירות בתנאי. אין צורך ב-if בתוכה, כי ההשוואה עצמה נותנת ערך בוליאני.

אם רוצים להחזיר יותר מערך אחד, כותבים ביניהם פסיק. הערכים מגיעים בטאפל אחד, ואפשר לפזר אותם למשתנים, כמו ב-v, i = 12, 0.5 מסעיף 2.6. נחשב למשל את המתח הגבוה ביותר ואת הזרם הגבוה ביותר שנגד בהספק נתון עומד בהם:

def limits(r, p):
    v = (p * r) ** 0.5
    i = (p / r) ** 0.5
    return round(v, 1), round(i, 3)

volts, amps = limits(470, 0.25)
print(volts, "V")
print(amps, "A")
print(limits(100, 1))
פלט
10.8 V
0.023 A
(10.0, 0.1)
טעות נפוצה: ענף בפעולה שאינו מגיע ל-return מחזיר None בלי שום הודעה. אם כותבים return בתוך if ולא כותבים דבר למקרה האחר, הפעולה תחזיר None במקרה ההוא.
שאלה 7.4: ההתנגדות השקולה של שני נגדים במקביל היא מכפלתם חלקי סכומם. מכיוון שהזימון שווה לערך, אפשר להציב אותו כפרמטר בזימון אחר. מה מדפיסה התוכנית הבאה?
def parallel(r1, r2):
    return r1 * r2 / (r1 + r2)

print(parallel(100, 300))
print(parallel(parallel(100, 300), 75))

7.5 פעולות שאינן מחזירות ערך

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

def rule(n, ch="-"):
    print(ch * n)

rule(12)
rule(6, "=")
x = rule(3)
print(x)
פלט
------------
======
---
None
דוגמה 7.4
הפקודה return בלי ערך מסיימת את הפעולה באמצעה, וזה שימושי כשאי אפשר להמשיך. נציג את הזרם, וכשההתנגדות אפס לא נחלק בה.
פתרון:
def show_current(v, r):
    if r == 0:
        print("Short circuit")
        return
    print("I =", v / r, "A")

show_current(12, 0)
show_current(12, 480)
פלט
Short circuit
I = 0.025 A
טעות נפוצה: כשמדפיסים זימון של פעולה שאינה מחזירה ערך, כמו print(rule(4)), מופיע קודם מה שהיא מדפיסה, ואחר כך המילה None. אם מחשבים איתו, התוכנית נעצרת. זה מה שראינו ב-r = r.sort() בסעיף 6.10: פעולה שמשנה את הרשימה אינה מחזירה דבר.
שאלה 7.5: מה מדפיסה התוכנית הבאה?
def mark(v, top=5):
    if v > top:
        print(v, "high")
        return
    print(v, "ok")

mark(3)
mark(7)
mark(7, 8)
print(mark(9))

7.6 זימון פעולות

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

התוכנית המזמנתהפעולהprint("start")a = current(12, 480)print(a)def current(v, r):i = v / rreturn i12341הזימון: שני הפרמטרים מקבלים את ערכיהם, v = 12 ו-r = 4802גוף הפעולה מתבצע מלמעלה למטה3return: חוזרים לאותה שורה עם הערך, ו-a מקבל אותו4התוכנית ממשיכה מן השורה הבאה
מסלול הזימון והחזרה: השורה הצבועה מסתיימת רק אחרי החזרה

פעולה מזמנת פעולה. זו החלוקה שדיברנו עליה, וראינו אותה בסעיף 7.4: is_ok מזמנת את deviation, והחזרה מגיעה תמיד למי שזימן.

דוגמה 7.5
הערכים במעבדה מגיעים ביחידות שונות: התנגדות בקילו-אוהם, וזרם שרוצים במיליאמפר. נכתוב פעולה שממירה כל ערך ליחידת הבסיס, ונשתמש בה עם חוק אום. כשיש זימון בתוך זימון, הפנימי מחושב קודם.
פתרון:
def to_base(value, prefix):
    if prefix == "k":
        return value * 1000
    if prefix == "m":
        return value / 1000
    return value

print(12 / to_base(4.8, "k"))
print(to_base(250, "m"))
print(round(to_base(9, "") / to_base(4.7, "k") * 1000, 2), "mA")
פלט
0.0025
0.25
1.91 mA

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

טעות נפוצה: בלי סוגריים אין זימון. print(current) נותנת את הפעולה עצמה כערך ואינה מפעילה אותה, ולכן מודפסים תיאורה וכתובתה בזיכרון: <function current at 0x000001F2A4C8>
שאלה 7.6: מה מדפיסה התוכנית הבאה, ובאיזה סדר?
def twice(x):
    print("twice", x)
    return 2 * x

def plus(a, b):
    print("plus", a, b)
    return a + b

print(plus(twice(1), twice(3)))

7.7 טבלת מעקב

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

def power(v, i):
    p = v * i
    return p

a = power(12, 2)
b = power(5, 3)
print(a + b)
מעקב אחר פעולה שמזומנת פעמיים
פקודהabpower.vpower.ipower.ppower()פלט
a = power(12, 2)
p = v * i12224
return p2424
b = power(5, 3)
p = v * i5315
return p1515
print(a + b)39
דוגמה 7.6
פעולה עם תנאי מוסיפה לטבלה גם עמודת תנאי. עם ערך ברירת מחדל הפרמטר מופיע בערכו גם אם לא ציינו אותו בזימון. נשים לב ששני הזימונים יצאו דרך return שונה.
פתרון:
def limit(v, top=5):
    if v > top:
        return top
    return v

x = limit(7)
y = limit(3)
print(x, y)
פקודהxylimit.vlimit.toplimit()v > topפלט
x = limit(7)
if v > top:75True
return top55
y = limit(3)
if v > top:35False
return v33
print(x, y)5 3
טעות נפוצה: מעבירים ערך מזימון לזימון. משתנה מקומי אינו זוכר דבר: אילו היה בפעולה מונה שמתחיל מאפס, הוא היה מתחיל מאפס בכל זימון.
שאלה 7.7: בנו טבלת מעקב לתוכנית הבאה, וכתבו את הפלט.
def fix(a, b=1):
    c = a + b
    if c > 5:
        c = c - 5
    return c

m = fix(3, 4)
print(m)

7.8 פעולות המקבלות מבני נתונים, פעולות המחזירות מבני נתונים

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

def deviation(nominal, measured):
    return abs(measured - nominal) * 100 / nominal

def good_parts(nominal, batch, tol=5):
    good = []
    for m in batch:
        if deviation(nominal, m) <= tol:
            good.append(m)
    return good

batch = [1020, 940, 1049, 1100]
print(good_parts(1000, batch))
print(good_parts(1000, batch, 10))
print(len(good_parts(1000, batch, 1)))
print(batch)
פלט
[1020, 1049]
[1020, 940, 1049, 1100]
0
[1020, 940, 1049, 1100]
דוגמה 7.7
בסעיף 7.3 חישבנו את הנגד של הנורית, אבל ההתנגדות שחושבה לא תמיד נמכרת. נכתוב מחדש את led_series כך שתחזיר את הערך במקום להדפיס אותו, ופעולה שנייה שתבחר מסדרת הערכים התקניים את הקרוב ביותר.
פתרון:
def led_series(vs, vf=2.0, i=0.02):
    return (vs - vf) / i

def nearest(values, target):
    best = values[0]
    for v in values:
        if abs(v - target) < abs(best - target):
            best = v
    return best

e12 = [100, 120, 150, 180, 220, 270,
       330, 390, 470, 560, 680, 820]
needed = led_series(12)
print(needed)
print(nearest(e12, needed))
print(nearest(e12, led_series(9)))
פלט
500.0
470
330

זה החיפוש אחר ערך קיצון מסעיף 6.12, אבל הקריטריון כאן הוא המרחק מן היעד.

מה קורה לרשימה בתוך הפעולה?:

הפרמטר מצביע על אותה רשימה שהעברנו, ולא על עותק שלה, כמו שראינו ב-b = a בסעיף 6.4. לכן zero_negatives משנה את הרשימה המקורית, לעומת good_parts, שהשאירה אותה כמו שהיא והחזירה רשימה חדשה. בוחרים לפי התפקיד, ואומרים זאת בשם הפעולה:

def zero_negatives(values):
    for i in range(len(values)):
        if values[i] < 0:
            values[i] = 0

a = [3, -1, 2, -5]
zero_negatives(a)
print(a)
פלט
[3, 0, 2, 0]
טעות נפוצה: שינוי של איבר מגיע לרשימה המקורית, אבל השמה לפרמטר עצמו משנה רק את המשתנה המקומי. בדוגמה הבאה x = 0 לא שינתה את n, השורה items[0] = 0 שינתה את הרשימה המקורית, והשורה items = [] גרמה לפרמטר להצביע על רשימה אחרת ולא שינתה דבר בחוץ:
def reset(x, items):
    x = 0
    items[0] = 0
    items = []

n = 5
b = [5, 6]
reset(n, b)
print(n, b)
פלט
5 [0, 6]
שאלה 7.8: מה מדפיסה התוכנית הבאה?
def pick(values, k):
    result = []
    for i in range(0, len(values), k):
        result.append(values[i])
    return result

def bump(values):
    values[0] += 1

a = [1, 2, 3, 4, 5]
b = pick(a, 2)
bump(b)
print(a)
print(b)

7.9 שימוש באופרטור * כדי לארוז ולפרק פרמטרים (Packing and Unpacking)

הפעולה print מקבלת כל מספר של ערכים. בעזרת האופרטור * גם אנחנו נכתוב פעולות כאלה, ויש לו שני תפקידים לפי מקומו:

איפה כותבים אותודוגמהמה הוא עושה
בהגדרה, לפני פרמטרdef series(*resistors):אריזה: אוסף את כל הערכים לטאפל אחד
בזימון, לפני רשימהseries(*parts)פירוק: מחלק את האיברים לערכים נפרדים

ההתנגדות השקולה של נגדים בטור היא סכומם, ומספרם משתנה ממעגל למעגל:

def series(*resistors):
    print(resistors)
    return sum(resistors)

print(series(100, 220))
print(series(100, 220, 470))
print(series())
פלט
(100, 220)
320
(100, 220, 470)
790
()
0

בתוך הפעולה resistors הוא טאפל רגיל, ויכול להיות גם ריק. לפניו יכולים לבוא פרמטרים רגילים שמקבלים את הערכים הראשונים, וכל מה שנשאר נכנס לטאפל, כמו ב-def report(name, *values):.

פירוק הוא הכיוון ההפוך: יש רשימה, והפעולה רוצה ערכים נפרדים. current(*pair) נותן את מה שנותן current(pair[0], pair[1]) בלי לספור אינדקסים, ומספר האיברים חייב להיות שווה למספר הפרמטרים. עם print מודפסים איברי הרשימה בלי סוגריים:

def current(v, r):
    return v / r

def series(*resistors):
    return sum(resistors)

pair = [12, 480]
print(current(pair[0], pair[1]))
print(current(*pair))
parts = [100, 220, 470]
print(series(*parts))
print(parts)
print(*parts)
print(*parts, sep=" + ")
פלט
0.025
0.025
790
[100, 220, 470]
100 220 470
100 + 220 + 470
טעות נפוצה: שוכחים את הכוכבית בזימון וכותבים series(parts). אז הרשימה כולה נכנסת כערך אחד לטאפל, ו-sum נעצרת, כי אי אפשר לחבר רשימה עם מספר.
שאלה 7.9: מה מדפיסה התוכנית הבאה?
def top(limit, *values):
    n = 0
    for v in values:
        if v > limit:
            n += 1
    return n

data = [4, 9, 6]
print(top(5, 4, 9, 6))
print(top(5, *data))
print(top(*data))
print(top(5))

7.10 טווח ההכרה של משתנים (שימוש ב-global) (global)

טווח הכרה: החלק בתוכנית שבו המשתנה מוכר.
משתנה מקומי: משתנה שנולד בתוך פעולה, וביניהם הפרמטרים. הוא קיים כל עוד הפעולה פועלת, ואינו מוכר מחוץ לה.
משתנה גלובלי: משתנה שנולד מחוץ לכל הפעולות. הוא מוכר בכל התוכנית, וכל פעולה יכולה לקרוא אותו בלי שום הצהרה.
טווח הכרה גלובליlimit = 11.8def check(v):טווח הכרה מקומיv = 12.4gap = 0.6def show(v):טווח הכרה מקומיv = 5gap = 2קריאה בלבד
לכל פעולה טווח משלה: gap בפעולה הראשונה ו-gap בשנייה הם שני משתנים שונים, ושתיהן יכולות לקרוא את limit

משתנה מקומי נעלם כשהפעולה מסתיימת, ואם מדפיסים אותו אחריה התוכנית נעצרת בשגיאה NameError. אותו שם בתוך הפעולה ומחוצה לה הוא שני משתנים נפרדים. נראה זאת בטבלת המעקב: העמודה current.i משתנה, והעמודה i אינה זזה:

i = 99

def current(v, r):
    i = v / r
    return i

a = current(12, 480)
print(a, i)
פקודהiacurrent.icurrent()פלט
i = 9999
a = current(12, 480)
i = v / r0.025
return i0.0250.025
print(a, i)0.025 99
טעות נפוצה: השמה בתוך פעולה הופכת את השם למקומי בכל הפעולה. לכן כשהצד הימני בתוכנית הבאה מנסה לקרוא את energy, עדיין אין לו ערך, והתוכנית נעצרת בשגיאה UnboundLocalError:
energy = 0

def add_load(watts, hours):
    energy = energy + watts * hours

add_load(60, 2)

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

energy = 0

def add_load(watts, hours):
    global energy
    energy = energy + watts * hours

add_load(60, 2)
add_load(100, 0.5)
print(energy, "Wh")
פלט
170.0 Wh

התוכנית עובדת, אבל מי שקורא את add_load(60, 2) לא יודע שהיא משנה משהו בחוץ, וכשיש הרבה פעולות לא יודעים איזו מהן שינתה את המשתנה. לכן עדיף להעביר את הערך כפרמטר ולהחזיר את התוצאה, וכך הכול נראה בשורת הזימון:

def add_load(energy, watts, hours):
    return energy + watts * hours

total = 0
total = add_load(total, 60, 2)
total = add_load(total, 100, 0.5)
print(total, "Wh")
פלט
170.0 Wh
שאלה 7.10: מה מדפיסה התוכנית הבאה?
a = 1
b = 2

def f(a):
    global b
    a = a + 10
    b = b + 10
    c = a + b
    return c

print(f(a))
print(a, b)

7.11 הדגמה: שימוש בפעולה main כנקודת הכניסה לקוד התוכנית

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

קובץ שכתבנו יכול להיות מיובא לתוכנית אחרת בפקודה import, כדי להשתמש בפעולות שלו. אבל הייבוא מריץ את כל מה שיש בקובץ, גם פקודות שמחוץ לפעולות. כדי למנוע זאת, פייתון נותנת לכל קובץ משתנה בשם __name__: ערכו "__main__" כשמריצים את הקובץ עצמו, ושם הקובץ בלי הסיומת .py כשהוא מיובא. ננסה עם קובץ בשם ohm.py, ונריץ אותו ישירות:

def current(v, r):
    return v / r

print("name is", __name__)

if __name__ == "__main__":
    print(current(12, 480))
פלט
name is __main__
0.025

עכשיו נייבא אותו מתוכנית אחרת באותה תיקייה. הפקודה הלא מוגנת רצה, ומה שמתחת לתנאי לא רץ. לכן הפקודה שמתחת לתנאי היא בדרך כלל main():

import ohm

print(ohm.current(5, 250))
פלט
name is ohm
0.02
דוגמה 7.8
נרכיב עכשיו את ארגז הנגדים מסעיף 7.1. כל מלבן בחלוקה נהיה פעולה, ו-main הקצרה מחברת ביניהן ונקראת כמו התרשים שציירנו.
פתרון:
def deviation(nominal, measured):
    return abs(measured - nominal) * 100 / nominal

def read_batch():
    batch = []
    value = float(input("Measured (0 to stop): "))
    while value != 0:
        batch.append(value)
        value = float(input("Measured (0 to stop): "))
    return batch

def split_batch(nominal, batch, tol=5):
    good = []
    bad = []
    for m in batch:
        if deviation(nominal, m) <= tol:
            good.append(m)
        else:
            bad.append(m)
    return good, bad

def report(good, bad):
    print("Good:", len(good), good)
    print("Reject:", len(bad), bad)

def main():
    nominal = float(input("Nominal: "))
    batch = read_batch()
    good, bad = split_batch(nominal, batch)
    report(good, bad)

if __name__ == "__main__":
    main()
פלט
Nominal: 1000
Measured (0 to stop): 1020
Measured (0 to stop): 940
Measured (0 to stop): 1049
Measured (0 to stop): 1100
Measured (0 to stop): 0
Good: 2 [1020.0, 1049.0]
Reject: 2 [940.0, 1100.0]
טעות נפוצה: כותבים def main(): ולא מזמנים אותה. אז התוכנית רצה בלי שגיאה ואינה מדפיסה דבר, כי ההגדרה אינה מבצעת דבר, כמו שראינו בסעיף 7.2.
שאלה 7.11: (א) מה מדפיסה print(__name__) בקובץ שמריצים ישירות?
(ב) מה מדפיסה אותה שורה בקובץ ohm.py כשהוא מיובא?

7.12 הוראת pass (כמציין מיקום עבור קוד עתידי) (pass)

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

def read_sample():
    pass

def save_sample(sample):
    pass

def main():
    sample = read_sample()
    save_sample(sample)
    print("Skeleton runs", sample)

main()
פלט
Skeleton runs None

השלד רץ, ומכאן שהשמות ומספר הפרמטרים נכונים. הערך None מזכיר לנו שפעולה שגופה pass אינה מחזירה דבר. ממלאים את הפעולות אחת אחת. אפשר גם לגרום ל-read_sample להחזיר ערך קבוע בינתיים, כמו return 25.0, וכך לבדוק את מה שאחריה עוד לפני שיש בידינו חיישן.

טעות נפוצה: pass אינה עוצרת דבר ואינה מדלגת על דבר: התוכנית ממשיכה לשורה הבאה, לעומת break שיוצאת מן הלולאה ו-continue שעוברת לסבב הבא.
שאלה 7.12: (א) מה מחזירה f() אם הגוף של f הוא pass?
(ב) האם אפשר להשתמש ב-pass בגוף של if?
▶ניסוי חי — מעבדת הפעולותמריצים במעבדה תוכניות עם פעולות צעד אחר צעד, ורואים איך הביצוע קופץ אל הפעולה וחוזר ממנה, ומה הערך של כל משתנה מקומי. עובדים בה ערכי ברירת מחדל, global, pass, החזרה של יותר מערך אחד, * בהגדרה ובזימון, ו-__name__. אבל input ו-import אינם מוכרים למפרש של המעבדה, ולכן כותבים את הערכים במשתנים במקום לקרוא אותם, ומשאירים את הפעולות באותו קובץ במקום לייבא אותן.
⚠
המעבדה אינה זמינה כרגע. החומר בפרק שלם גם בלעדיה: ממשיכים לקרוא, וחוזרים לניסוי מאוחר יותר או פותחים אותו ישירות מן הקישור שמתחת למסגרת.
https://fahed-lab.com/electronics/python_basics#funcs
פותחים את המעבדה בדף נפרד ↗ fahed-lab.com/electronics/python_basics#funcs
פרויקט הספר: תחנת המדידה · שלב 7 — התחנה מסודרת בפעולות

עד עכשיו כתבנו לתחנה תוכניות נפרדות: המרת קריאת הממיר בפרק 2, ההחלטה בפרק 3 ופענוח השורה בפרק 5. עכשיו נאחד אותן לתוכנית אחת, וכל אחת מהן היא פעולה עם תפקיד אחד: adc_to_volts ממירה את קריאת הממיר, parse_line מפענחת את השורה ובודקת אותה, decide מיישמת את כללי פרק 3: מנתקת את העומס מתחת ל-11.8 V, ומפעילה את המאוורר מעל 40 מעלות כשהעומס פועל.

main()adc_to_voltsparse_lineis_numberreportdecide
מי מזמן את מי בתוכנית התחנה
def adc_to_volts(adc, scale=1):
    return adc * 5 / 1023 * scale

def is_number(text):
    digits = text.replace(".", "")
    return text.count(".") <= 1 and digits.isnumeric()

def parse_line(line):
    battery = -1.0
    temp = -1.0
    for item in line.split(";"):
        pos = item.find("=")
        text = item[pos + 1:]
        if pos == -1 or not is_number(text):
            return False, 0, 0
        if item[:pos] == "BAT":
            battery = float(text)
        elif item[:pos] == "TMP":
            temp = float(text)
    return battery >= 0 and temp >= 0, battery, temp

def decide(battery, temp):
    load = battery >= 11.8
    fan = temp > 40 and load
    return load, fan

def report(battery, temp):
    load, fan = decide(battery, temp)
    print("Battery:", round(battery, 2), "V")
    print("Temp:", round(temp, 1), "C")
    print("Load on:", load, " Fan on:", fan)

def main():
    adc_batt = int(input("ADC battery: "))
    adc_temp = int(input("ADC temp: "))
    battery = adc_to_volts(adc_batt, 4)
    temp = adc_to_volts(adc_temp, 100)
    report(battery, temp)
    line = input("Line: ")
    ok, battery, temp = parse_line(line)
    if ok:
        report(battery, temp)
    else:
        print("Line rejected")

if __name__ == "__main__":
    main()
פלט
ADC battery: 620
ADC temp: 82
Battery: 12.12 V
Temp: 40.1 C
Load on: True  Fan on: True
Line: BAT=12.42;TMP=31.5
Battery: 12.42 V
Temp: 31.5 C
Load on: True  Fan on: False

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

השורההפלט
BAT=11.5;TMP=46Battery: 11.5 V , Temp: 46.0 C , Load on: False Fan on: False
BAT=12.42;TMP=hotLine rejected
BAT=12.42Line rejected
BAT12.42;TMP=31.5Line rejected

והרווח העיקרי הוא שאפשר לבדוק כל פעולה לבדה, בלי שום קלט:

הזימוןהתוצאה
adc_to_volts(1023)5.0
is_number("3.1.4")False
parse_line("BAT=12.42;TMP=31.5")(True, 12.42, 31.5)
parse_line("BAT=12.42")(False, 12.42, -1.0)
decide(11.5, 46)(False, False)
decide(12.4, 46)(True, True)

נסו בעצמכם: רוצים שסף ניתוק העומס יהיה פרמטר עם ערך ברירת מחדל, cutoff=11.8. איזו פעולה משנים? ואילו פעולות נשארות כמו שהן?

עיקרי הפרק:

סיכום פקודות הפרק:

פקודהמה היא עושה
def name(a, b):הגדרת פעולה עם שני פרמטרים
name(1, 2) name(b=2, a=1)זימון לפי הסדר, או לפי שם הפרמטר
def f(a, b=2):פרמטר עם ערך ברירת מחדל, אחרי הפרמטרים הרגילים
return xהחזרת ערך וסיום הפעולה
return a, b x, y = f()החזרת שני ערכים כטאפל, ופיזורם
return Noneיציאה בלי ערך, ומה שחוזר אז
def f(*values):אריזת מספר לא ידוע של ערכים בטאפל
f(*items) print(*items)פירוק רשימה לערכים נפרדים
global xהשמה למשתנה הגלובלי מתוך פעולה
if __name__ == "__main__":מתקיים כשמריצים את הקובץ עצמו, ואינו מתקיים כשמייבאים אותו
passפקודה שאינה עושה דבר וממלאת גוף ריק

מילון מונחים:

עבריתEnglishערבית
פעולה · פונקציהFunctionالعمليّة · الدالة
עידוןStepwise refinementالتفصيل التدريجي
גוף הפעולהFunction bodyجسم العمليّة
זימוןCallالاستدعاء
פרמטרParameterالوسيط
ערך ברירת מחדלDefault valueالقيمة الافتراضية
ערך מוחזרReturn valueالقيمة المُعادة
מחרוזת תיעודDocstringجملة التوثيق
משתנה מקומיLocal variableالمتغيّر المحلي
משתנה גלובליGlobal variableالمتغيّر العام
טווח הכרהScopeمدى الرؤية
אריזה ופירוקPacking and unpackingالحزم والفكّ
הפעולה הראשיתMain functionالعمليّة الرئيسية
שלדSkeletonالهيكل
התשובות הסופיות לשאלות הפרק
7.1 למשל: read_temps קוראת את הקריאות לרשימה, highest מחזירה את הקריאה הגבוהה ביותר, count_above מחזירה את מספר הקריאות מעל הסף. כל חלוקה סבירה נכונה.
7.2 True , False. מדפיסה True אם ברשימה יש לפחות n איברים שערכם 1, ואחרת False.
7.3 6 , 12 , 7 , 13
7.4 75.0 , 37.5
7.5 3 ok , 7 high , 7 ok , 9 high , None
7.6 twice 1 , twice 3 , plus 2 6 , 8
7.7
פקודהmfix.afix.bfix.cfix()c > 5פלט
m = fix(3, 4)
c = a + b347
if c > 5:True
c = c - 52
return c22
print(m)2
7.8 [1, 2, 3, 4, 5] , [2, 3, 5]
7.9 2 , 2 , 2 , 0
7.10 23 , 1 12
7.11 (א) __main__ , (ב) ohm
7.12 (א) None , (ב) כן, בכל מקום שצריך גוף מוזח.

שאלות לתרגול:

רמה ראשונה — בסיסי
1.
מה מדפיסה התוכנית?
def f(x, y=3):
    return x * y

def g(x):
    print(f(x) + 1)

g(2)
print(f(2, 2))
print(g(1))
תשובות: 7 , 4 , 4 , None
2.
התוכנית הבאה נעצרת בשגיאה. מצאו את הסיבה ותקנו.
def half_power(v, r):
    p = v * v / r
    print(p / 2)

result = half_power(12, 48)
print("Half power:", round(result, 2))
תשובות: הפעולה מדפיסה ואינה מחזירה, ולכן result מקבל את הערך None. כותבים return:
def half_power(v, r):
    p = v * v / r
    return p / 2

result = half_power(12, 48)
print("Half power:", round(result, 2))
והיא מדפיסה: Half power: 1.5
3.
כתבו פעולה drop שמקבלת את הזרם ואחריו את ההתנגדות, ומחזירה את מפל המתח על הנגד. ברירת המחדל של ההתנגדות היא 100 Ω.
תשובות:
def drop(i, r=100):
    return i * r

print(drop(0.02))
print(drop(0.02, 470))
מדפיסה: 2.0 , 9.4
4.
כתבו פעולה שמקבלת רשימת קריאות ושני גבולות, ומחזירה את מספר הקריאות שבין הגבולות, כולל הגבולות.
תשובות:
def count_between(values, low, high):
    count = 0
    for v in values:
        if low <= v <= high:
            count += 1
    return count

print(count_between([4.7, 5.1, 5.0, 3.3], 4.9, 5.1))
מדפיסה: 2
רמה שנייה — בינוני
5.
כדי לוודא שמסגרת נתונים תקינה, שולחים איתה את סכום הבתים שלה מודולו 256. כתבו שתי פעולות: checksum מחזירה את הסכום הזה, ו-is_intact מזמנת אותה ומחזירה אם המסגרת תקינה.
תשובות:
def checksum(frame):
    return sum(frame) % 256

def is_intact(frame, received):
    return checksum(frame) == received

print(checksum([200, 100, 7]))
print(is_intact([200, 101, 7], 51))
מדפיסה: 51 , False
6.
בנו טבלת מעקב לתוכנית הבאה.
def step(a, b):
    a = a + b
    b = b * 2
    return a, b

x, y = step(1, 2)
x, y = step(x, y)
print(x, y)
תשובות:
פקודהxystep.astep.bstep()פלט
x, y = step(1, 2)
a = a + b32
b = b * 24
return a, b34(3, 4)
x, y = step(x, y)
a = a + b74
b = b * 28
return a, b78(7, 8)
print(x, y)7 8
7.
כתבו פעולה שמקבלת כל מספר של ערכים ומחזירה את הקטן ביותר. זמנו אותה פעם עם ערכים נפרדים, ופעם עם רשימה שמפרקים.
תשובות:
def lowest(*values):
    best = values[0]
    for v in values:
        if v < best:
            best = v
    return best

volts = [12.1, 11.7, 12.6]
print(lowest(9, 5, 7))
print(lowest(*volts))
מדפיסה: 5 , 11.7
8.
קוראים ערך של נגד משלושת הפסים הראשונים שלו: שני הפסים הראשונים הם ספרות, והשלישי הוא חזקת העשר. כתבו פעולה שמקבלת רשימה של שמות צבעים ומחזירה את הערך באוהם. חפשו כל צבע בטאפל בעזרת הפעולה index.
תשובות:
def band_ohms(bands):
    colors = ("black", "brown", "red", "orange", "yellow",
              "green", "blue", "violet", "gray", "white")
    a = colors.index(bands[0])
    b = colors.index(bands[1])
    k = colors.index(bands[2])
    return (a * 10 + b) * 10 ** k

print(band_ohms(["yellow", "violet", "red"]))
print(band_ohms(["brown", "black", "orange"]))
print(band_ohms("red red brown".split()))
מדפיסה: 4700 , 10000 , 220
רמה שלישית — אתגר
9.
כתבו פעולה to_bits שמחזירה מחרוזת עם הספרות הבינריות של מספר, ברוחב קבוע שברירת המחדל שלו 8 סיביות, בלי להשתמש ב-bin. בדקו אותה מול bin.
תשובות:
def to_bits(n, width=8):
    bits = ""
    for k in range(width):
        bits = str(n % 2) + bits
        n = n // 2
    return bits

print(to_bits(5))
print(to_bits(202) == bin(202)[2:])
מדפיסה: 00000101 , True
10.
כתבו פעולה network שמקבלת את סוג החיבור, "s" לטור או "p" למקביל, וכל מספר של נגדים. חשבו בעזרתה מעגל מורכב: 47 Ω בטור עם שני נגדים של 100 Ω במקביל.
תשובות:
def network(kind, *rs):
    if kind == "s":
        return sum(rs)
    total = 0
    for r in rs:
        total += 1 / r
    return round(1 / total, 2)

print(network("s", 100, 220))
print(network("p", 100, 100, 100))
print(network("s", 47, network("p", 100, 100)))
מדפיסה: 320 , 33.33 , 97.0
11.
התוכנית הבאה משתמשת ב-global. כתבו אותה בלעדיו, עם פרמטר וערך מוחזר, כך שהפלט יישאר אותו דבר.
count = 0

def beep():
    global count
    count += 1
    print("beep", count)

beep()
beep()
תשובות:
def beep(count):
    count += 1
    print("beep", count)
    return count

count = 0
count = beep(count)
count = beep(count)
מדפיסה: beep 1 , beep 2
12.
הרחיבו את nearest מסעיף 7.8: כתבו פעולה standard שמחזירה את הערך התקני הקרוב ביותר לכל נגד, גדול ככל שיהיה. מחלקים בעשר עד שהערך נכנס לדקדה הראשונה, מחפשים, ואז כופלים.
תשובות:
def nearest(values, target):
    best = values[0]
    for v in values:
        if abs(v - target) < abs(best - target):
            best = v
    return best

def standard(r):
    e12 = [10, 12, 15, 18, 22, 27, 33,
           39, 47, 56, 68, 82, 100]
    scale = 1
    while r >= 100:
        r = r / 10
        scale = scale * 10
    return nearest(e12, r) * scale

print(standard(500))
print(standard(1950))
מדפיסה: 470 , 1800

שאלות בגרות לפרק 7

ארבע השאלות הבאות בנויות כולן על פעולות, ונפתור אותן במלואן. כל טבלת מעקב כאן נוצרה בהרצה אמיתית של הקוד. בכותרות העמודות כתבנו את שם המשתנה בלבד, כמו שהשאלה מבקשת.
בגרות 2026 שאלה 9 — הספרה הראשונה והספרה האחרונה
בגרות 2026
סעיףהמבוקשתשובה
(א)הפלט של שלוש הפקודות.2 , 4 , 6
(ב)רשימה שהפעולה מחזירה עבורה 0, ורשימה שהיא מחזירה עבורה 5.למטה
(ג)הפעולה sum_of_digits.למטה
הפתרון המלא: fahed-lab.com/exams/electronics-815381/815381/solutions/solution-2026

(א) במספר תלת-ספרתי, lst[i] // 100 היא ספרת המאות ו-lst[i] % 10 היא ספרת האחדות, כמו בסעיף 2.9. אם כן, הפעולה סופרת את המספרים שהספרה הראשונה שלהם שווה לאחרונה. את שלושת המספרים נמצא בטבלת התשובות למעלה.

(ב) מספיקה דוגמה אחת לכל ערך. ברשימה [100, 123, 450] אין מספר ששתי ספרות הקצה שלו שוות, ולכן הפעולה מחזירה 0. ברשימה [101, 212, 323, 434, 545] כל המספרים כאלה, והיא מחזירה 5. שתי התוצאות מהרצת הפעולה.

(ג) את הסכום הזה חישבנו ישירות למספר אחד בסוף פרק 2. החישוב אותו חישוב, אבל עכשיו הוא פעולה: הפרמטר במקום הערך הקבוע, ו-return במקום print:

def sum_of_digits(num):
    hundreds = num // 100
    tens = (num // 10) % 10
    ones = num % 10
    return hundreds + tens + ones

print(sum_of_digits(472))
print(sum_of_digits(900))
פלט
13
9
בגרות 2025 שאלה 9 — השוואת איברים מקבילים
בגרות 2025
סעיףהמבוקשתשובה
(א)הפלט של ארבעת הזימונים.HHLL , LHHL , LLHH , HLLH
(ב)שתי רשימות שעבורן התוכנית מדפיסה HHHH.למטה
(ג)הפעולה IsTheSame.למטה
הפתרון המלא: fahed-lab.com/exams/electronics-815381/815381/solutions/solution-2025

(א) זו פעולה שאינה מחזירה ערך: היא מדפיסה H כששני האיברים באותו אינדקס שווים, ו-L כשהם שונים, הכול בשורה אחת בזכות end="", ועוברת לשורה הבאה בפקודה print() האחרונה. נעקוב אחר הזימון השני, ואת ארבע השורות נמצא בטבלת התשובות למעלה:

מעקב אחר DoSomething(lst1, lst3)
פקודהilst1[i] == lst2[i]פלט
lst1 = [4, 5, 2, 3]
lst3 = [3, 5, 2, 4]
DoSomething(lst1, lst3)
for i in range(len(lst1)):0
if lst1[i] == lst2[i]:False
print("L", end="")L
for i in range(len(lst1)):1
if lst1[i] == lst2[i]:True
print("H", end="")H
for i in range(len(lst1)):2
if lst1[i] == lst2[i]:True
print("H", end="")H
for i in range(len(lst1)):3
if lst1[i] == lst2[i]:False
print("L", end="")L
for i in range(len(lst1)):
print()

(ב) התשובה HHHH פירושה שכל זוג שווה, כלומר כל שתי רשימות זהות בנות ארבעה איברים, כמו [7, 8, 9, 1] עם עצמה.

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

def IsTheSame(lst1, lst2):
    if len(lst1) != len(lst2):
        return False
    for i in range(len(lst1)):
        if lst1[i] != lst2[i]:
            return False
    return True

lst1 = [1, 2, 3, 4, 5]
lst2 = [1, 2, 3, 4, 5]
lst3 = [1, 2, 4, 4, 5]
b1 = IsTheSame(lst1, lst2)
b2 = IsTheSame(lst1, lst3)
print(b1, b2)
print(IsTheSame(lst1, [1, 2, 3]))
פלט
True False
False
בגרות 2024 שאלה 9 — ספרת האחדות והמקום
בגרות 2024
סעיףהמבוקשתשובה
(א)טבלת מעקב לרשימה הנתונה, והערך המוחזר.False
(ב)רשימה שעבורה מוחזר True ורשימה שעבורה מוחזר False.למטה
(ג)תפקיד הפעולה במשפט אחד.למטה
(ד)הפעולה IsInArray.למטה
הפתרון המלא: fahed-lab.com/exams/electronics-815381/815381/solutions/solution-2024

(א) הפקודה return False נמצאת בתוך הלולאה, ולכן ברגע שהתנאי מתקיים פעם אחת יוצאים מן הפעולה כולה ולא מסיימים את הרשימה. השאלה מבקשת עמודה גם לתנאי הלולאה. ללולאת for אין תנאי כתוב, אבל השורה שלה שואלת: האם נשאר איבר לקחת? את התשובה רושמים בעמודה for item in lst, ובעמודה item רואים מה נלקח. העמודה return היא הערך המוחזר:

מעקב אחר DoSomething(testList)
פקודהiitemreturnfor item in lstitem % 10 != iפלט
print(DoSomething(testList))
i = 00
for item in lst:20True
if item % 10 != i:False
i = i + 11
for item in lst:41True
if item % 10 != i:False
i = i + 12
for item in lst:2True
if item % 10 != i:False
i = i + 13
for item in lst:93True
if item % 10 != i:False
i = i + 14
for item in lst:14True
if item % 10 != i:False
i = i + 15
for item in lst:894True
if item % 10 != i:True
return FalseFalseFalse

באיבר 894 ספרת האחדות היא 4 והמשתנה i שווה ל-5, ולכן התנאי מתקיים והפעולה מחזירה False בלי להגיע לארבעת האיברים שנותרו.

(ב) מוחזר True כשספרת האחדות שווה למקום האיבר ברשימה, ומוחזר False כשהם שונים במקום כלשהו. הרשימה [50, 11, 72, 3] מחזירה True, והרשימה [50, 11, 73, 3] מחזירה False.

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

(ד) אותו מבנה: יוצאים עם True באיבר השווה הראשון, ומחזירים False רק אחרי שהלולאה הסתיימה. הטעות הנפוצה היא else: return False בתוך הלולאה, ואז הפעולה נעצרת באיבר הראשון. השאלה כותבת true, ובפייתון זה True:

def IsInArray(lst, num):
    for item in lst:
        if item == num:
            return True
    return False

lst = [2, 3, 5, 6, 2, 4, 2]
b1 = IsInArray(lst, 2)
b2 = IsInArray(lst, 7)
print(b1, b2)
פלט
True False
בגרות 2023 שאלה 10 — השוואת שתי רשימות
בגרות 2023
סעיףהמבוקשתשובה
(א)טבלת מעקב והפלט.True
(ב)הגדרת הבעיה שהפעולה פותרת עבור שתי רשימות שוות בגודלן.למטה
(ג)הפעולה countEven (בנוסח הערבי CountEven).למטה
הפתרון המלא: fahed-lab.com/exams/electronics-815381/815381/solutions/solution-2023

(א) נוסיף לקוד את השורה main_10() שמפעילה אותו. שתי הרשימות שוות באורכן, ולכן מגיעים ל-else, שם s אוסף את ההפרשים בין האיברים המקבילים. לא כותבים עמודה לשתי הרשימות, כי השאלה אינה מבקשת זאת ודבר בהן אינו משתנה:

פקודהibxyssod()len(arr1) != len(arr2)s != 0פלט
main_10()
a1 = [10,15,15,5,5]
a2 = [10,10,10,10,10]
print(sod(a1,a2))
b = TrueTrue
if len(arr1) != len(arr2):False
s = 00
i = 00
for x in arr1:10
y = arr2[i]10
s = s + x - y
i = i + 11
for x in arr1:15
y = arr2[i]
s = s + x - y5
i = i + 12
for x in arr1:
y = arr2[i]
s = s + x - y10
i = i + 13
for x in arr1:5
y = arr2[i]
s = s + x - y5
i = i + 14
for x in arr1:
y = arr2[i]
s = s + x - y0
i = i + 15
for x in arr1:
if s != 0:False
return bTrueTrue

בסוף s שווה לאפס, ולכן b אינו משתנה, והפלט הוא True.

(ב) s הוא סכום הרשימה הראשונה פחות סכום השנייה. לכן: הפעולה מקבלת שתי רשימות שוות בגודלן, ומחזירה True אם סכומי האיברים שלהן שווים, ואחרת מחזירה False. הסדר אינו חשוב, והאיברים יכולים להיות שונים. לכן עבור הרשימות [1, 2, 3] ו-[3, 2, 1] הפעולה מחזירה True.

(ג) מונה עם תנאי מפרק 4, בתוך פעולה שמקבלת רשימה ומחזירה מספר:

def countEven(lst):
    count = 0
    for x in lst:
        if x % 2 == 0:
            count += 1
    return count

print(countEven([10, 15, 18, 2, 17]))
print(countEven([1, 3]))
print(countEven([]))
פלט
3
0
0