יסודות התכנות בשפת Python · פרק 4 — ביצוע חוזר⌂📚🏠
פרק 4

ביצוע חוזר

30 שעות לימוד

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

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

4.1 הצורך בביצוע חוזר

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

total = 0
value = float(input("Sample: "))
total += value
value = float(input("Sample: "))
total += value
value = float(input("Sample: "))
total += value
print("Average:", total / 3)

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

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

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

total = 0
n = 1
while n <= 3:
    value = float(input("Sample: "))
    total += value
    n += 1
print("Average:", total / 3)
פלט
Sample: 20.5
Sample: 21
Sample: 21.5
Average: 21.0

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

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

  1. נתחיל במתח 12 V ובמספר פרקי זמן השווה לאפס.
  2. כל עוד המתח אינו קטן מ-1 V: נחלק את המתח ב-2, ונגדיל את מספר פרקי הזמן באחד.
  3. נדפיס את מספר פרקי הזמן ואת המתח.

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

volts = 12.0
steps = 0
while volts >= 1:
    volts = volts / 2
    steps += 1
print("Steps:", steps)
print("Volts:", volts)
פלט
Steps: 4
Volts: 0.75
שאלה 4.1: אילו פקודות הייתה התוכנית חוזרת עליהן אילו ביקשנו לקרוא 50 ערכים בלי לולאה, וכמה פעמים היינו כותבים אותן? ומדוע העתקות אינן מתאימות אם מספר הקריאות ידוע למשתמש רק בזמן הריצה?

4.2 תחביר בסיסי של לולאת while

הפקודה while פירושה "כל עוד". כותבים אותה כך:

while condition:
    statement
    statement
next_statement
הכלל: בכל לולאה תקינה יש שלושה חלקים: אתחול לפני הלולאה, שנותן למשתנה את ערכו הראשון, תנאי שמחליט אם ממשיכים, ועדכון בתוך הגוף, שמשנה את המשתנה ומקרב אותנו לסוף הלולאה. אם נשכח אחד מהם, הלולאה לא תפעל כמו שאנחנו רוצים.
אתחולהתנאיגוף הלולאהעדכוןהפקודה הבאהTrueFalse
תרשים זרימה של לולאת while: החץ שחוזר אל התנאי הוא מה שמבדיל אותה מ-if
דוגמה 4.2
בדיקה עצמית בהפעלת מכשיר: הנורית מהבהבת שלוש פעמים, ואז מופיעה הודעה. נכתוב את התוכנית ונבנה לה טבלת מעקב.
פתרון:
blink = 1
while blink <= 3:
    print("LED blink", blink)
    blink += 1
print("Test finished")
פלט
LED blink 1
LED blink 2
LED blink 3
Test finished
מעקב אחר התוכנית
פקודהblinkblink <= 3פלט
blink = 11
while blink <= 3:True
print("LED blink", blink)LED blink 1
blink += 12
while blink <= 3:True
print("LED blink", blink)LED blink 2
blink += 13
while blink <= 3:True
print("LED blink", blink)LED blink 3
blink += 14
while blink <= 3:False
print("Test finished")Test finished

נקרא את הטבלה מלמעלה למטה. התנאי מופיע ארבע פעמים והגוף שלוש פעמים: שלוש פעמים התנאי התקיים, ובפעם הרביעית לא התקיים והלולאה הסתיימה. וביציאה הערך של blink הוא 4, ולא 3.

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

volts = 3.0
while volts > 5:
    print("Too high")
    volts -= 1
print("Volts:", volts)
פלט
Volts: 3.0

והעדכון אינו תמיד הוספה של אחד. אפשר להוסיף צעד בכל גודל, כמו volts += 3, ולהפחית בספירה לאחור, כמו left -= 1.

כתיבה אלגוריתמית ומימוש בקוד:

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

התיאור במיליםהקודמה הקוד הוסיף
חזור חמש פעמיםn = 1 , while n <= 5: , n += 1מונה עם אתחול ועדכון
כל עוד המתח קטן מ-12 Vwhile volts < 12:הערך הראשון של volts ועדכונו
עד שהמשתמש יכתוב -1while x != -1:היפוך התנאי, ופקודת קריאה בתוך הגוף

נשים לב להבדל בשורה האחרונה: בדיבור אומרים "עד ש" ומציינים מתי עוצרים, ואילו while שואל מתי ממשיכים. נחזור לתרגום הזה בסעיף 4.5.

טעות נפוצה: סדר הפקודות בגוף משנה את התוצאה. כאן מעדכנים את n לפני ההדפסה, ומקבלים 2, 3, 4 במקום 1, 2, 3:
n = 1
while n <= 3:
    n += 1
    print("Sample", n)
פלט
Sample 2
Sample 3
Sample 4

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

n = 1
while n <= 3:
    n += 1
print("Sample", n)
פלט
Sample 4
שאלה 4.2: מה מדפיסה התוכנית הבאה?
n = 1
total = 0
while n < 4:
    total += n
    n += 1
print(n, total)

4.3 מסננת קלט עקשנית (do while)

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

מסננת עקשנית: לולאה שאינה נותנת למשתמש להתקדם עד שיקליד ערך תקין. ביציאה ממנה מובטח שהערך תקין.

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

הדרך הראשונה: קריאה לפני הלולאה וקריאה בתוכה:

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

adc = int(input("ADC (0-1023): "))
while adc < 0 or adc > 1023:
    print("Out of range, try again")
    adc = int(input("ADC (0-1023): "))
print("Accepted:", adc)
פלט
ADC (0-1023): 1500
Out of range, try again
ADC (0-1023): -4
Out of range, try again
ADC (0-1023): 512
Accepted: 512
מעקב אחר התוכנית לקלטים 1500 ו-512
פקודהadcadc < 0 or adc > 1023פלט
adc = int(input("ADC (0-1023): "))1500ADC (0-1023): 1500
while adc < 0 or adc > 1023:True
print("Out of range, try again")Out of range, try again
adc = int(input("ADC (0-1023): "))512ADC (0-1023): 512
while adc < 0 or adc > 1023:False
print("Accepted:", adc)Accepted: 512

הדרך השנייה: לולאה עם התנאי True ויציאה בפקודה break:

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

while True:
    adc = int(input("ADC (0-1023): "))
    if 0 <= adc <= 1023:
        break
    print("Out of range, try again")
print("Accepted:", adc)
קריאה כפולהwhile True
מספר פקודות הקריאהשתי פקודות זהותפקודה אחת
התנאי מתאראת הערך הנדחה, וממשיכים כל עוד הוא מתקייםאת הערך המתקבל, ויוצאים כשהוא מתקיים
מתי נעדיףכשהתנאי קצר והקריאה פשוטהכשהקריאה ארוכה או שקודמת לה הודעה

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

טעות נפוצה: הפקודה if במקום while נותנת הזדמנות אחת בלבד. הערך השני מתקבל בלי בדיקה, גם אם אף הוא מחוץ לתחום:
adc = int(input("ADC (0-1023): "))
if adc < 0 or adc > 1023:
    adc = int(input("ADC (0-1023): "))
print("Accepted:", adc)
פלט
ADC (0-1023): 1500
ADC (0-1023): 2000
Accepted: 2000
שאלה 4.3: כתבו מסננת עקשנית שקוראת אחוז הפעלה duty בין 0 ל-100, ואז מדפיסה אותו.

4.4 קליטת כמות לא ידועה של נתונים (תבנית קלט עם זקיף)

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

זקיף: ערך מיוחד, מחוץ לתחום הנתונים התקינים, שמודיע שהנתונים הסתיימו. הוא אינו חלק מן הנתונים, ולכן אינו נכנס לחישוב.
הכלל: קוראים את הערך הראשון לפני הלולאה. התנאי בודק שהערך אינו הזקיף. מטפלים בערך בגוף, ובסוף הגוף קוראים את הערך הבא.
דוגמה 4.3
נקרא הספקים של מכשירים המחוברים לקו אחד, ונדפיס את סכומם. הספק אפס אינו מכשיר, ולכן נבחר באפס כזקיף.
פתרון:
total = 0
watts = float(input("Load W (0 stops): "))
while watts != 0:
    total += watts
    watts = float(input("Load W (0 stops): "))
print("Total:", total, "W")
פלט
Load W (0 stops): 60
Load W (0 stops): 100
Load W (0 stops): 25
Load W (0 stops): 0
Total: 185.0 W
מעקב אחר התוכנית לקלטים 60, 25 ו-0
פקודהtotalwattswatts != 0פלט
total = 00
watts = float(input("Load W (0 stops): "))60.0Load W (0 stops): 60
while watts != 0:True
total += watts60.0
watts = float(input("Load W (0 stops): "))25.0Load W (0 stops): 25
while watts != 0:True
total += watts85.0
watts = float(input("Load W (0 stops): "))0.0Load W (0 stops): 0
while watts != 0:False
print("Total:", total, "W")Total: 85.0 W

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

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

טעות נפוצה: קוראים בתחילת הגוף ומחברים אחר כך, ולכן הזקיף נכנס לחישוב. כאן גם נאלצים לתת ערך ראשון מדומה כדי שהתוכנית תתחיל. הסכום הנכון הוא 30, אבל התוכנית מדפיסה ערך קטן באחד:
total = 0
pulses = 1
while pulses != -1:
    pulses = int(input("Pulses (-1 stops): "))
    total += pulses
print("Total:", total)
פלט
Pulses (-1 stops): 10
Pulses (-1 stops): 20
Pulses (-1 stops): -1
Total: 29

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

count = 0
part = input("Part (end stops): ")
while part != "end":
    count += 1
    print("Stored:", part)
    part = input("Part (end stops): ")
print("Parts:", count)
פלט
Part (end stops): LM358
Stored: LM358
Part (end stops): 7805
Stored: 7805
Part (end stops): end
Parts: 2
שימו לב: בוחרים זקיף מחוץ לתחום הנתונים. הערך -1 אינו מתאים לקריאת טמפרטורה, כי -1 מעלות הוא ערך אמיתי בחורף, והתוכנית תעצור מוקדם מדי. נבחר ב-999, שרחוק מכל טמפרטורה שהחיישן מודד.
שאלה 4.4: מה מדפיסה התוכנית הבאה לקלטים 3, 5 ו-0? ומה תדפיס אם הקלט הראשון הוא 0?
x = int(input("x: "))
y = 0
while x != 0:
    y += x * 2
    x = int(input("x: "))
print(y)

4.5 תרגום תנאי סיום לתנאי הלולאה

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

הכלל: תנאי הלולאה הוא השלילה של תנאי הסיום. ממשיכים כל עוד תנאי הסיום אינו מתקיים.
עוצרים כאשרממשיכים כל עודמה קרה
x == -1x != -1שוויון הופך לאי-שוויון
total >= 100total < 100הופכים את ההשוואה, ולא שוכחים את מקרה השוויון
v < 0 or v > 5v >= 0 and v <= 5הופכים כל השוואה, ו-or הופך ל-and
a == 0 and b == 0a != 0 or b != 0הופכים כל השוואה, ו-and הופך ל-or

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

abnot (a or b)not a and not bnot (a and b)not a or not b
FalseFalseTrueTrueTrueTrue
FalseTrueFalseFalseTrueTrue
TrueFalseFalseFalseTrueTrue
TrueTrueFalseFalseFalseFalse

העמודות השלישית והרביעית זהות, וכך גם החמישית והשישית.

דוגמה 4.4
יחידת תקשורת אוספת מנות לזיכרון. היא מפסיקה לקלוט כשמספר הבתים מגיע ל-256 או אחרי 5 מנות. נתרגם את התנאי ואז נכתוב את התוכנית.
פתרון:
התנאי
עוצרים כאשרtotal >= 256 or packets >= 5
ממשיכים כל עודtotal < 256 and packets < 5
total = 0
packets = 0
while total < 256 and packets < 5:
    size = int(input("Packet bytes: "))
    total += size
    packets += 1
print("Bytes:", total)
print("Packets:", packets)
פלט
Packet bytes: 100
Packet bytes: 120
Packet bytes: 90
Bytes: 310
Packets: 3

התוכנית עצרה כאן בגלל הבתים, אחרי שלוש מנות. ועם חמש מנות קטנות של 10 בתים כל אחת היא עוצרת בגלל המספר, ומדפיסה: Bytes: 50 , Packets: 5

ואם התרגום קשה, אפשר לכתוב את השלילה כמו שהיא באופרטור not סביב תנאי הסיום: while not (total >= 256 or packets >= 5):. אותו חישוב ואותה תוצאה.

טעות נפוצה: שוכחים ש-or הופך ל-and, וכותבים or בין התנאים. אז הלולאה ממשיכה כל עוד אחד מהם לא הגיע לגבול שלו, כלומר עד ששניהם מתקיימים יחד. עבור אותן שלוש מנות התוכנית אינה עוצרת, אלא מבקשת עוד שתי מנות, ובסוף מדפיסה: Bytes: 330 , Packets: 5

והטעות הזו מסוכנת יותר בצורה אחרת. נרצה מסננת שמקבלת את המצב 1 או 2, ונכתוב while mode != 1 or mode != 2:. הלולאה הזו לא מסתיימת לעולם: כל מספר שונה מ-1 או שונה מ-2, ולכן התנאי מתקיים תמיד, גם כשהמשתמש כותב ערך נכון. נחזור אל הכלל: עוצרים כאשר mode == 1 or mode == 2, ולכן נכתוב while mode != 1 and mode != 2:.

דוגמה 4.5
מנעול אלקטרוני נותן שלושה ניסיונות להקלדת הקוד. הוא עוצר כשמגיע הקוד הנכון או אחרי שלושה ניסיונות. אחרי הלולאה נשאל למה עצרנו, כי לכל סיבה יש הודעה משלה.
פתרון:
code = int(input("Code: "))
tries = 1
while code != 4071 and tries < 3:
    code = int(input("Code: "))
    tries += 1
if code == 4071:
    print("Unlocked")
else:
    print("Locked")
פלט
Code: 1111
Code: 4071
Unlocked

ואחרי שלושה ניסיונות שגויים היא מדפיסה: Locked

תרגול מהיר — שלילת תנאים כאשר a = 2 ו-b = 5
1not (a > 3 or b > 3)
2a <= 3 and b <= 3
3not (a > 3 and b > 3)
4a <= 3 or b <= 3
5not a == 2
6not (a == 2 or b == 2)
תשובות: 1 False 2 False 3 True 4 True 5 False 6 False

4.6 משימות חישוב טיפוסיות

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

מונה:

מונה: משתנה שמתחיל מערך התחלתי, בדרך כלל אפס, וגדל באחד בכל פעם שקורה משהו שרוצים לספור.
דוגמה 4.6
כמה סיביות דרושות לייצוג מספר מסוים? כל חלוקת שלמים ב-2 מורידה סיבית אחת, ולכן נספור את החלוקות עד שהמספר מגיע לאפס. זו הסיבה שממיר שהקריאה הגבוהה ביותר שלו היא 1023 הוא ממיר של עשר סיביות.
פתרון:
value = int(input("Largest value: "))
bits = 0
while value > 0:
    value = value // 2
    bits += 1
print("Bits needed:", bits)
פלט
Largest value: 1023
Bits needed: 10

ועבור הערך 1024 היא מדפיסה: Bits needed: 11, כי 1024 דורש סיבית נוספת.

מונה עם תנאי:

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

דוגמה 4.7
נקרא ערכי זרם בקו שההגנה שלו 10 A, ונספור כמה פעמים הערכים חרגו מן הגבול הזה.
פתרון:
limit = 10
over = 0
amps = float(input("Current (-1 stops): "))
while amps != -1:
    if amps > limit:
        over += 1
    amps = float(input("Current (-1 stops): "))
print("Over the limit:", over)
פלט
Current (-1 stops): 8.5
Current (-1 stops): 12
Current (-1 stops): 10.4
Current (-1 stops): -1
Over the limit: 2

צובר:

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

ראינו צובר בסעיף 4.4, כשחיברנו את ההספקים של המכשירים בפקודה total += watts.

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

דוגמה 4.8
מגבר של כמה דרגות המחוברות בטור. ההגבר הכולל הוא מכפלת ההגברים של הדרגות. נתחיל מ-1, כי מכפלה של כל מספר באפס היא אפס.
פתרון:
gain = 1
stage = float(input("Stage gain (0 stops): "))
while stage != 0:
    gain *= stage
    stage = float(input("Stage gain (0 stops): "))
print("Total gain:", gain)
פלט
Stage gain (0 stops): 10
Stage gain (0 stops): 5
Stage gain (0 stops): 0
Total gain: 50.0

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

count = 0
total = 0
temp = float(input("Temp (999 stops): "))
while temp != 999:
    count += 1
    total += temp
    temp = float(input("Temp (999 stops): "))
if count > 0:
    print("Average:", round(total / count, 2))
else:
    print("No readings")
פלט
Temp (999 stops): 22.5
Temp (999 stops): 24
Temp (999 stops): 19.5
Temp (999 stops): 999
Average: 22.0

ערכי קיצון:

ערך קיצון: הערך הגדול ביותר או הקטן ביותר בסדרת ערכים. שומרים את הטוב ביותר שמצאנו עד כה, ומשווים אליו כל ערך חדש.
דוגמה 4.9
היציאה של ספק כוח צריכה להיות ישרה, אבל יש בה אדווה קטנה. גודל האדווה הוא ההפרש בין הקריאה הגבוהה ביותר לנמוכה ביותר. נקרא מתחים עד אפס, ונדפיס את ההפרש הזה. נקבע שהקריאה הראשונה היא גם הגדולה וגם הקטנה, כי עד הרגע הזה כך הדבר.
פתרון:
v = float(input("Volts (0 stops): "))
high = v
low = v
while v != 0:
    if v > high:
        high = v
    if v < low:
        low = v
    v = float(input("Volts (0 stops): "))
print("Ripple:", round(high - low, 2), "V")
פלט
Volts (0 stops): 11.9
Volts (0 stops): 12.3
Volts (0 stops): 11.7
Volts (0 stops): 0
Ripple: 0.6 V

הזקיף אינו נכנס להשוואה, כי התנאי נבדק מיד אחרי קריאתו.

טעות נפוצה: מתחילים מאפס את החיפוש אחר הגדול ביותר. התוכנית מצליחה כל עוד הערכים חיוביים, וטועה בטמפרטורות של חורף: הקריאה הגבוהה ביותר היא -3, אבל היא מדפיסה ערך שלא קראה בכלל:
top = 0
temp = int(input("Temp (999 stops): "))
while temp != 999:
    if temp > top:
        top = temp
    temp = int(input("Temp (999 stops): "))
print("Highest:", top)
פלט
Temp (999 stops): -7
Temp (999 stops): -3
Temp (999 stops): -12
Temp (999 stops): 999
Highest: 0

הפתרון הוא שהערך הראשון יהיה קריאה אמיתית: קוראים קודם, ואז כותבים top = temp לפני הלולאה, והתוכנית מדפיסה Highest: -3.

שילוב בין המשימות:

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

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

hour = 0
total = 0
watts = float(input("Panel W (-1 stops): "))
while watts != -1:
    hour += 1
    total += watts
    if hour == 1 or watts > peak:
        peak = watts
        peak_hour = hour
    watts = float(input("Panel W (-1 stops): "))
if hour > 0:
    print("Average:", round(total / hour, 1), "W")
    print("Peak:", peak, "W at hour", peak_hour)
פלט
Panel W (-1 stops): 60
Panel W (-1 stops): 110
Panel W (-1 stops): 135
Panel W (-1 stops): 90
Panel W (-1 stops): -1
Average: 98.8 W
Peak: 135.0 W at hour 3

נשים לב לתנאי hour == 1 or watts > peak: בשעה הראשונה עדיין אין peak, אבל קיצור הדרך בחישוב מסעיף 3.5 מונע את חישוב האגף הימני, ולכן אין שגיאה.

המשימהלפני הלולאהבתוך הלולאה
מונהcount = 0count += 1
מונה עם תנאיcount = 0if ok: count += 1
צובר בחיבורtotal = 0total += x
צובר בכפלgain = 1gain *= x
הגדולtop = firstif x > top: top = x
הקטןlow = firstif x < low: low = x
ממוצעצובר ומונהאחרי הלולאה: total / count
שאלה 4.5: מה מדפיסה התוכנית הבאה לקלטים 3, 7, 5, 9 ו-0? מה מייצג כל אחד מן המשתנים a ו-b?
a = 0
b = 0
x = int(input("x: "))
while x != 0:
    a += 1
    if x > 5:
        b += x
    x = int(input("x: "))
print(a, b)

4.7 הפקודות break ו-continue

פקודהמה היא עושה
breakיוצאת מן הלולאה מיד, ועוברת לפקודה שאחריה
continueמדלגת על שאר הגוף בסבב הזה, וחוזרת לבדיקת התנאי
התנאיתנאי פנימישאר הגוףהפקודה הבאהTrueFalseFalsebreak
הפקודה break קופצת אל מה שאחרי הלולאה
התנאיתנאי פנימישאר הגוףהפקודה הבאהTrueFalseFalsecontinue
הפקודה continue קופצת אל התנאי

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

דוגמה 4.11
נבדוק סוללה של שישה תאים. נעצור בתא החלש הראשון, שמתחו קטן מ-3 V: אין צורך למדוד את השאר, כי הסוללה הזו תיכנס לתיקון בכל מקרה.
פתרון:
cell = 1
while cell <= 6:
    v = float(input("Cell V: "))
    if v < 3.0:
        print("Weak cell:", cell)
        break
    cell += 1
print("Scan finished")
פלט
Cell V: 3.7
Cell V: 3.6
Cell V: 2.8
Weak cell: 3
Scan finished

אפשר לכתוב את אותה תוכנית בלי break, בעזרת דגל בוליאני weak שנשנה ל-True בתא החלש, ותנאי מורכב שהוא התרגום של "עוצרים בתא חלש או אחרי שישה תאים": while cell <= 6 and not weak:

הפקודה break מקצרת את התוכנית, והתנאי המורכב מראה את סיבת היציאה בראש הלולאה. שתי הדרכים מקובלות.

דוגמה 4.12
נקרא קריאות ממיר עד -1, ונחשב ממוצע. קריאה גדולה מ-1023 נובעת מרעש, ולכן נתעלם ממנה ולא נכניס אותה לחישוב.
פתרון:
total = 0
count = 0
while True:
    adc = int(input("ADC (-1 stops): "))
    if adc == -1:
        break
    if adc > 1023:
        print("Ignored:", adc)
        continue
    total += adc
    count += 1
if count > 0:
    print("Average:", total / count)
else:
    print("No readings")
פלט
ADC (-1 stops): 500
ADC (-1 stops): 2000
Ignored: 2000
ADC (-1 stops): 520
ADC (-1 stops): -1
Average: 510.0

בסבב השני ביצעה התוכנית continue, ולכן לא הגיעה לחיבור ולא למונה.

טעות נפוצה: שמים את continue לפני עדכון המונה, ולכן המונה אינו משתנה לעולם אחרי הקפיצה, והתוכנית מסתובבת במקום בערוץ השלישי:
ch = 1
while ch <= 4:
    if ch == 3:
        continue
    print("Channel", ch)
    ch += 1

מעדכנים את המונה בתחילת הגוף, לפני כל קפיצה:

ch = 0
while ch < 4:
    ch += 1
    if ch == 3:
        continue
    print("Channel", ch)
פלט
Channel 1
Channel 2
Channel 4
שאלה 4.6: מה מדפיסה התוכנית הבאה? בנו טבלת מעקב ובדקו.
n = 0
while n < 6:
    n += 1
    if n == 2:
        continue
    if n == 5:
        break
    print(n)
print("end", n)

4.8 ביצוע אינסופי

ביצוע אינסופי: לולאה שהתנאי שלה אינו מפסיק להתקיים לעולם, ולכן אינה מסתיימת מעצמה. אם זה קורה בטעות, התוכנית נראית תקועה. עוצרים אותה בצירוף המקשים Ctrl+C, וב-Google Colab בלחצן העצירה שליד התא.

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

n = 1
while n <= 3:
    print("Sample", n)
שכחנו את העדכון: ערך המונה נשאר כמו שהוא
n = 1
while n <= 3:
    print("Sample", n)
n += 1
העדכון בלי הזחה, ולכן הוא מחוץ לגוף ולא מגיעים אליו
volts = 12
while volts > 0:
    print(volts)
    volts += 3
העדכון בכיוון הלא נכון: המתח מתרחק מן הסוף
x = 0.0
while x != 1.0:
    x += 0.1
תנאי שאי אפשר להגיע אליו

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

x = 0.0
n = 0
while n < 10:
    x += 0.1
    n += 1
print(x)
print(x == 1.0)
פלט
0.9999999999999999
False

לא כותבים != בתנאי לולאה על מספר עשרוני שהגיע מחישוב. בודקים קרבה בעזרת סבילות, או סופרים את הסבבים במספר שלם כמו בתוכנית למעלה. התנאי while abs(x - 1.0) > 0.001: מסתיים אחרי עשרה סבבים בדיוק. זקיף שנקרא מן הקלט, כמו בסעיף 4.4, הוא מקרה אחר: לא חישבנו אותו, ולכן ההשוואה אליו מדויקת.

הכלל: בכל לולאה נשאל: מה משתנה בכל סבב? והאם השינוי הזה באמת מקרב אותנו לרגע שבו התנאי אינו מתקיים?

לולאה אינסופית מכוונת:

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

while True:
    adc = read_sensor()
    show_on_display(adc)
    wait_one_second()

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

temp = 20
minutes = 0
while temp < 60 and minutes < 30:
    temp += 6
    minutes += 1
if temp >= 60:
    print("Ready after", minutes, "min")
else:
    print("Heater fault")
פלט
Ready after 7 min
שאלה 4.7: אילו מן הלולאות הבאות מסתיימות, ואילו לא? ולמה?
k = 10
while k > 0:
    k = k - 4
(א)
k = 10
while k != 0:
    k = k - 4
(ב)
k = 1
while k > 0:
    k = k + 1
(ג)

4.9 ניתוח נכונות בעזרת טבלת מעקב

נכונות: שהתוכנית נותנת את התוצאה הנדרשת לכל קלט מותר, ולא רק לדוגמה שניסינו.

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

דוגמה 4.13
התוכנית הבאה צריכה לחשב ממוצע של מתחים עד -1, אבל עבור הערכים 10 ו-20 היא נותנת 10.0 במקום 15.0. נחפש את הטעות.
פתרון:
count = 1
total = 0
v = int(input("V (-1 stops): "))
while v != -1:
    total += v
    count += 1
    v = int(input("V (-1 stops): "))
print(total / count)
מעקב אחר התוכנית לקלטים 10, 20 ו-‎-1
פקודהcounttotalvv != -1פלט
count = 11
total = 00
v = int(input("V (-1 stops): "))10V (-1 stops): 10
while v != -1:True
total += v10
count += 12
v = int(input("V (-1 stops): "))20V (-1 stops): 20
while v != -1:True
total += v30
count += 13
v = int(input("V (-1 stops): "))-1V (-1 stops): -1
while v != -1:False
print(total / count)10.0

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

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

count = 0
total = 0
v = int(input("V (-1 stops): "))
while v != -1:
    total += v
    count += 1
    v = int(input("V (-1 stops): "))
print(total / count)
פלט
V (-1 stops): -1
ZeroDivisionError: division by zero

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

if count > 0:
    print(total / count)
else:
    print("No readings")
הבדיקההקלטהצפויהפלט האחרון
מקרה רגיל10, 20, -115.015.0
ערך אחד10, -110.010.0
אין ערכים-1הודעהNo readings
ערכים שווים7, 7, 7, -17.07.0
הערה: בדיקות חושפות טעויות, אבל אינן מוכיחות שאין כאלה. לכן בוחרים אותן בחוכמה: מקרה רגיל, המקרה הקטן ביותר, ומקרה שנמצא בדיוק על הגבול.

ויש טעות קצה נפוצה מאוד, טעות של סבב אחד. הלולאה הבאה צריכה להדפיס שלוש קריאות, אבל n < 3 עוצר לפני השלישית:

n = 1
while n < 3:
    print("Sample", n)
    n += 1
print("n =", n)
פלט
Sample 1
Sample 2
n = 3

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

▶ניסוי חי — מעבדת הלולאותנריץ את הלולאה צעד אחר צעד, ונראה את התנאי נבדק ואת ערכי המשתנים משתנים, ממש כמו בטבלת מעקב. במעבדה אין קלט מן המשתמש, ולכן כותבים את הערכים ישירות במשתנים במקום input.
⚠
המעבדה אינה זמינה כרגע. החומר בפרק שלם גם בלעדיה: ממשיכים לקרוא, וחוזרים לניסוי מאוחר יותר או פותחים אותו ישירות מן הקישור שמתחת למסגרת.
https://fahed-lab.com/electronics/python_basics#loops
פותחים את המעבדה בדף נפרד ↗ fahed-lab.com/electronics/python_basics#loops

יעילות:

יעילות: כמה עבודה התוכנית צריכה כדי להגיע לתוצאה. שתי תוכניות נכונות יכולות להיות שונות מאוד ביעילותן. נמדוד אותה כאן בדרך פשוטה: מספר הסבבים.
דוגמה 4.14
ממיר מחפש מספר בין 0 ל-1023 שמתאים למתח בכניסה שלו. הדרך הראשונה עולה מספר אחר מספר:
פתרון:
target = int(input("Value: "))
guess = 0
steps = 0
while guess < target:
    guess += 1
    steps += 1
print("Found", guess, "in", steps, "steps")
פלט
Value: 731
Found 731 in 731 steps

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

target = int(input("Value: "))
low = 0
high = 1023
steps = 0
while low < high:
    mid = (low + high) // 2
    if mid < target:
        low = mid + 1
    else:
        high = mid
    steps += 1
    print(low, high)
print("Found", low, "in", steps, "steps")
פלט
Value: 731
512 1023
512 767
640 767
704 767
704 735
720 735
728 735
728 731
730 731
731 731
Found 731 in 10 steps
המספר המבוקשהדרך הראשונההדרך השנייה
10Found 10 in 10 stepsFound 10 in 10 steps
731Found 731 in 731 stepsFound 731 in 10 steps
1023Found 1023 in 1023 stepsFound 1023 in 10 steps

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

הערה: דרכים פשוטות ליעילות: יוצאים מן הלולאה כשהתשובה ידועה, בפקודה break או בתנאי מורכב. ולא שמים בגוף חישוב שאינו משתנה מסבב לסבב. למשל, בתחנה מחשבים את step = 5 / 1023 פעם אחת לפני הלולאה.

4.10 קינון ושילוב מבני if, while

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

שרשרת תנאים בתוך לולאה:

דוגמה 4.15
ספק מתח במעבדה נשלט מתפריט. נחזור על השאלה עד שהמשתמש יבחר ביציאה. זו התבנית של כל תפריט: זקיף, ושרשרת elif בגוף.
פתרון:
volts = 12.0
choice = int(input("1 up, 2 down, 0 exit: "))
while choice != 0:
    if choice == 1:
        volts += 0.5
    elif choice == 2:
        volts -= 0.5
    else:
        print("Unknown choice")
    print("Output:", volts, "V")
    choice = int(input("1 up, 2 down, 0 exit: "))
print("Bye")
פלט
1 up, 2 down, 0 exit: 1
Output: 12.5 V
1 up, 2 down, 0 exit: 2
Output: 12.0 V
1 up, 2 down, 0 exit: 7
Unknown choice
Output: 12.0 V
1 up, 2 down, 0 exit: 0
Bye

לולאה בתוך לולאה:

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

דוגמה 4.16
נבנה טבלת זרמים במיליאמפר: כל שורה למתח (3, 6 ו-9 V), וכל עמודה לנגד (100, 200 ו-300 Ω). הפרמטר end משאיר אותנו באותה שורה, ו-print() הריק מסיים את השורה.
פתרון:
v = 3
while v <= 9:
    print(v, "V:", end=" ")
    r = 100
    while r <= 300:
        print(round(v / r * 1000, 1), end=" ")
        r += 100
    print()
    v += 3
פלט
3 V: 30.0 15.0 10.0 
6 V: 60.0 30.0 20.0 
9 V: 90.0 45.0 30.0 
טעות נפוצה: אילו העברנו את r = 100 אל לפני הלולאה החיצונית, היה r נשאר אחרי השורה הראשונה על 400, הלולאה הפנימית לא הייתה פועלת עוד, ושאר השורות היו ריקות: 3 V: 30.0 15.0 10.0 / 6 V: / 9 V: . מקום האתחול הוא בתוך הלולאה החיצונית, מיד לפני הפנימית.

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

row = 1
while row <= 2:
    col = 1
    while col < row:
        print(row, col)
        col += 1
    row += 1
התוכנית
פקודהrowcolrow <= 2col < rowפלט
row = 11
while row <= 2:True
col = 11
while col < row:False
row += 12
while row <= 2:True
col = 1
while col < row:True
print(row, col)2 1
col += 12
while col < row:False
row += 13
while row <= 2:False
מעקב אחר שתי לולאות מקוננות
שאלה 4.8: מה מדפיסה התוכנית הבאה?
a = 1
while a <= 3:
    b = a
    while b <= 3:
        print(a * b, end=" ")
        b += 1
    print()
    a += 1
שימו לב: הפקודה break יוצאת מן הלולאה הפנימית בלבד, והחיצונית ממשיכה כרגיל.
דוגמה 4.17
לוח מקשים של ארבע שורות ושלוש עמודות. הבקר בודק את המקשים בזה אחר זה עד שהוא מוצא את המקש הלחוץ. כדי לצאת משתי הלולאות יחד נשתמש בדגל בוליאני שמופיע בשני התנאים. ערכי המקש הלחוץ כתובים כאן ישירות בתוכנית.
פתרון:
key_row = 2
key_col = 2
found = False
row = 1
while row <= 4 and not found:
    col = 1
    while col <= 3 and not found:
        print("Testing", row, col)
        if row == key_row and col == key_col:
            found = True
        col += 1
    row += 1
print("Key found:", found)
פלט
Testing 1 1
Testing 1 2
Testing 1 3
Testing 2 1
Testing 2 2
Key found: True
פרויקט הספר: תחנת המדידה · שלב 4 — התחנה מודדת שוב ושוב

בשני הפרקים הקודמים התחנה קראה ערך אחד והחליטה. עכשיו היא אוספת קריאות ממיר של מתח הסוללה עד הזקיף -1, וכותבת דוח: מספר הדגימות, המתח הממוצע, הנמוך ביותר והגבוה ביותר, ומספר הדגימות מתחת ל-11.8 V. החישוב הוא זה שמפרק 2: צעד הממיר 5 / 1023, ומחלק המתח שמחלק ב-4.

step = 5 / 1023
limit = 11.8
count = 0
total = 0
below = 0
adc = int(input("ADC battery (-1 stops): "))
while adc != -1:
    volts = adc * step * 4
    count += 1
    total += volts
    if count == 1 or volts > top:
        top = volts
    if count == 1 or volts < bottom:
        bottom = volts
    if volts < limit:
        below += 1
    adc = int(input("ADC battery (-1 stops): "))
if count > 0:
    print("Samples:", count)
    print("Average:", round(total / count, 2), "V")
    print("Min:", round(bottom, 2), "V")
    print("Max:", round(top, 2), "V")
    print("Below limit:", below)
else:
    print("No samples")
פלט
ADC battery (-1 stops): 620
ADC battery (-1 stops): 633
ADC battery (-1 stops): 590
ADC battery (-1 stops): 578
ADC battery (-1 stops): -1
Samples: 4
Average: 11.83 V
Min: 11.3 V
Max: 12.38 V
Below limit: 2

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

עיקרי הפרק:

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

פקודהמה היא עושה
while condition:חוזרת על הגוף כל עוד התנאי מתקיים
while True:לולאה שמסתיימת רק בפקודה break
breakיציאה מיידית מן הלולאה הקרובה
continueדילוג על שאר הסבב וחזרה לתנאי
count += 1עדכון מונה
total += xעדכון צובר
while x != -1:המשך עד הזקיף
print(x, end=" ")הדפסה בלי מעבר לשורה חדשה

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

עבריתEnglishערבית
לולאהLoopالحلقة
ביצוע חוזרRepeated executionالتنفيذ المتكرر
גוף הלולאהLoop bodyجسم الحلقة
סבב (איטרציה)Iterationالدورة
מונהCounterالعدّاد
צוברAccumulatorالمُجمِّع
זקיףSentinelقيمة التوقّف
מסננת קלטInput filterمصفاة المُدخَلات
מסננת עקשניתPersistent input filterالمصفاة العنيدة
ערך קיצוןExtreme valueالقيمة القصوى
תנאי סיוםStop conditionشرط الإنهاء
חוקי דה-מורגןDe Morgan lawsقانونا دي مورغان
ביצוע אינסופיInfinite loopالحلقة اللانهائية
קינוןNestingالتداخل
נכונותCorrectnessصحّة البرنامج
מקרה קצהEdge caseالحالة الحدّية
יעילותEfficiencyالنجاعة
התשובות הסופיות לשאלות הפרק
4.1 כותבים את פקודת הקריאה ואת פקודת החיבור 50 פעמים כל אחת. מספר שידוע רק בזמן הריצה אי אפשר לכתוב מראש במספר השורות, ולכן דרושה לולאה.
4.2 4 6
4.3
duty = int(input("Duty (0-100): "))
while duty < 0 or duty > 100:
    duty = int(input("Duty (0-100): "))
print("Duty:", duty)
לקלטים 120, 75 היא מדפיסה: Duty: 75
4.4 16 , 0
4.5 4 16. המשתנה a הוא מונה של כל הערכים, ו-b הוא צובר של הערכים הגדולים מ-5.
4.6 1 , 3 , 4 , end 5
4.7 (א) מסתיימת, כי k מגיע לערך שלילי. (ב) לא מסתיימת: הערכים הם 10, 6, 2, -2 ואז שליליים, כלומר הם מדלגים על האפס ואינם שווים לו לעולם. (ג) לא מסתיימת: k גדל, ולמספר השלם בפייתון אין גבול עליון.
4.8 1 2 3 / 4 6 / 9

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

רמה ראשונה — בסיסי
1.
מה מדפיסה התוכנית?
k = 2
s = 0
while k < 10:
    s += k
    k = k + 3
print(k, s)
תשובות: 11 15
2.
תרגמו את תנאי הסיום לתנאי לולאה:
(א) עוצרים כאשר x == 0.
(ב) עוצרים כאשר t >= 100 or n == 20.
(ג) עוצרים כאשר a < 0 and b < 0.
תשובות: (א) x != 0 , (ב) t < 100 and n != 20 , (ג) a >= 0 or b >= 0
3.
כתבו מסננת עקשנית שקוראת מספר ערוץ מ-1 עד 8, ומדפיסה הודעה על כל מספר שגוי.
תשובות:
ch = int(input("Channel (1-8): "))
while ch < 1 or ch > 8:
    print("No such channel")
    ch = int(input("Channel (1-8): "))
print("Reading channel", ch)
לקלטים 9, 3 היא מדפיסה בסוף: Reading channel 3
4.
מצאו את שלוש הטעויות בתוכנית הזו ותקנו אותן. הדרישה היא להדפיס את כפולות 5 מ-5 עד 20.
n = 1
while n <= 4
print(n * 5)
n =+ 1
תשובות: חסרות נקודתיים אחרי התנאי, שתי הפקודות צריכות הזחה, והסימן הנכון הוא +=. הכתיב =+ הוא השמה של המספר החיובי 1, ומוביל לביצוע אינסופי:
n = 1
while n <= 4:
    print(n * 5)
    n += 1
היא מדפיסה: 5 , 10 , 15 , 20
רמה שנייה — בינוני
5.
בנו טבלת מעקב לתוכנית הבאה כשהקלט הוא 30.
x = int(input("x: "))
n = 0
while x > 1:
    x = x // 3
    n += 1
print(x, n)
תשובות:
פקודהxnx > 1פלט
x = int(input("x: "))30x: 30
n = 00
while x > 1:True
x = x // 310
n += 11
while x > 1:True
x = x // 33
n += 12
while x > 1:True
x = x // 31
n += 13
while x > 1:False
print(x, n)1 3
6.
קראו נגדים המחוברים במקביל עד אפס, והדפיסו את ההתנגדות השקולה.
1 R = 1 R1 + 1 R2 + ...
תשובות:
inverse = 0
r = float(input("R (0 stops): "))
while r != 0:
    inverse += 1 / r
    r = float(input("R (0 stops): "))
if inverse > 0:
    print("R total:", round(1 / inverse, 2))
לקלטים 100, 300, 0 היא מדפיסה: R total: 75.0
7.
מודדים מתח של נוריות עד אפס. נורית תקינה אם מתחה בין 1.8 V ל-2.2 V, כולל הקצוות. הדפיסו את מספר התקינות ואת אחוזן.
תשובות:
good = 0
count = 0
v = float(input("LED V (0 stops): "))
while v != 0:
    count += 1
    if 1.8 <= v <= 2.2:
        good += 1
    v = float(input("LED V (0 stops): "))
if count > 0:
    print("Good:", good)
    print("Percent:", round(good * 100 / count, 1))
לקלטים 2.0, 1.5, 2.2, 2.5, 0 היא מדפיסה: Good: 2 , Percent: 50.0
8.
סוללה מאבדת 3% מן הקיבולת הנוכחית שלה בכל שנה, ומתחילה מ-100%. אחרי כמה שנים תרד הקיבולת מתחת ל-80%?
תשובות:
capacity = 100
years = 0
while capacity >= 80:
    capacity = capacity * 0.97
    years += 1
print("Years:", years)
print("Capacity:", round(capacity, 1))
היא מדפיסה: Years: 8 , Capacity: 78.4
רמה שלישית — אתגר
9.
קראו טמפרטורות עד 999. הדפיסו את האורך של הרצף הארוך ביותר של קריאות עוקבות מעל 40 מעלות.
תשובות:
run = 0
best = 0
t = int(input("Temp (999 stops): "))
while t != 999:
    if t > 40:
        run += 1
        if run > best:
            best = run
    else:
        run = 0
    t = int(input("Temp (999 stops): "))
print("Longest hot run:", best)
לקלטים 41, 45, 30, 42, 43, 44, 20, 999 היא מדפיסה: Longest hot run: 3
10.
שתי נוריות מהבהבות יחד ברגע אפס. המחזור של הראשונה a שניות ושל השנייה b שניות, והם מספרים שלמים חיוביים. אחרי כמה שניות יהבהבו שוב יחד?
תשובות:
a = int(input("LED A period: "))
b = int(input("LED B period: "))
t = a
while t % b != 0:
    t += a
print("Together after:", t)
למחזורים 4, 6 היא מדפיסה: Together after: 12
11.
חפשו מחלק מתח שנותן מ-12 V יציאה קרובה ככל האפשר ל-5 V. נסו לכל אחד מן הנגדים R1 ו-R2 את כל הערכים מ-100 Ω עד 1000 Ω בצעד של 100 Ω.
Vout = 12 · R2 R1 + R2
תשובות:
goal = 5
best_err = 12
r1 = 100
while r1 <= 1000:
    r2 = 100
    while r2 <= 1000:
        vout = 12 * r2 / (r1 + r2)
        err = abs(vout - goal)
        if err < best_err:
            best_err = err
            best1 = r1
            best2 = r2
        r2 += 100
    r1 += 100
print("R1 =", best1, "R2 =", best2)
print("Error:", round(best_err, 3), "V")
היא מדפיסה: R1 = 700 R2 = 500 , Error: 0.0 V
12.
בדיקת זוגיות: קראו בתים עד -1. לכל בית ספרו את הסיביות שערכן 1 בחילוק חוזר ב-2, והדפיסו את מספרן ואת שארית חלוקתו ב-2.
תשובות:
value = int(input("Byte (-1 stops): "))
while value != -1:
    ones = 0
    rest = value
    while rest > 0:
        ones += rest % 2
        rest = rest // 2
    print(value, "ones:", ones, "parity:", ones % 2)
    value = int(input("Byte (-1 stops): "))
לבתים 5, 7 היא מדפיסה: 5 ones: 2 parity: 0 , 7 ones: 3 parity: 1

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

שתי השאלות הבאות דורשות רק את מה שלמדנו בפרק הזה, ולכן נפתור אותן במלואן.
בגרות 2024 שאלה 10 — חולצות וסווצ׳רים
בגרות 2024
סעיףהמבוקשתשובה
כל השאלהסופרים הזמנות של חולצות וסווצ׳רים עד מספר לא חוקי, ואז מדפיסים את שני המספרים ואת הסכום הכולל.למטה
הפתרון המלא: fahed-lab.com/exams/electronics-815381/815381/solutions/solution-2024

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

shirts = 0
sweaters = 0
choice = int(input("1 shirt, 2 sweater: "))
while choice == 1 or choice == 2:
    if choice == 1:
        shirts += 1
    else:
        sweaters += 1
    choice = int(input("1 shirt, 2 sweater: "))
total = shirts * 30 + sweaters * 50
print("Shirts:", shirts)
print("Sweaters:", sweaters)
print("Total:", total)
פלט
1 shirt, 2 sweater: 1
1 shirt, 2 sweater: 2
1 shirt, 2 sweater: 1
1 shirt, 2 sweater: 1
1 shirt, 2 sweater: 2
1 shirt, 2 sweater: 7
Shirts: 3
Sweaters: 2
Total: 190

השמות shirts ו-sweaters באנגלית כרגיל. נבדוק גם את מקרה הקצה: מספר לא חוקי כבר בהתחלה, והתוכנית מדפיסה בשורה האחרונה Total: 0.

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

תנאי הסיום מורכב: הסכום הכולל מגיע למיליון או מספר התורמים מגיע לאלף, ונתרגם אותו כמו בסעיף 4.5:

התנאי
עוצרים כאשרsum1 + sum2 >= 1000000 or donors >= 1000
ממשיכים כל עודsum1 + sum2 < 1000000 and donors < 1000

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

sum1 = 0
sum2 = 0
max1 = 0
max2 = 0
donors = 0
while sum1 + sum2 < 1000000 and donors < 1000:
    candidate = int(input("Candidate: "))
    amount = float(input("Amount: "))
    donors += 1
    if candidate == 1:
        sum1 += amount
        if amount > max1:
            max1 = amount
    else:
        sum2 += amount
        if amount > max2:
            max2 = amount
print("Candidate 1 sum:", sum1)
print("Candidate 2 sum:", sum2)
print("Donors:", donors)
print("Max Donation for Candidate 1:", max1)
print("Max Donation for Candidate 2:", max2)
פלט
Candidate: 1
Amount: 200000
Candidate: 2
Amount: 300000
Candidate: 1
Amount: 100000
Candidate: 1
Amount: 100000
Candidate: 2
Amount: 200000
Candidate: 1
Amount: 150000
Candidate 1 sum: 550000.0
Candidate 2 sum: 500000.0
Donors: 6
Max Donation for Candidate 1: 200000.0
Max Donation for Candidate 2: 300000.0

המספרים הם אלה שבשאלה. ההבדל היחיד הוא .0 בסופם, כי לפי נוסח השאלה הסכום הוא מספר ממשי, ולכן קראנו אותו בפקודה float. הלולאה עוצרת אחרי התורם השישי, כשהסכום מגיע ל-1050000.