ביצוע חוזר
מכשיר אמיתי אינו מודד פעם אחת ועוצר: התחנה קוראת את מתח הסוללה שוב ושוב, המטען בודק את הזרם כל עוד הוא פועל, והבקר חוזר על אותן פקודות כל היום. בפרק הזה נלמד את התוכנית לחזור על פקודות, לדעת מתי לעצור, ולהפיק מסדרה ארוכה של קריאות את מספרן, את סכומן, את הגדולה ואת הקטנה שבהן.
- נבחין בין תיאור לולאה במילים לבין כתיבתה בקוד, ונכתוב לולאת 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
ויש מקרה נוסף שאף העתקה לא פותרת, כי מספר הסבבים נובע מן החישוב עצמו:
- נתחיל במתח 12 V ובמספר פרקי זמן השווה לאפס.
- כל עוד המתח אינו קטן מ-1 V: נחלק את המתח ב-2, ונגדיל את מספר פרקי הזמן באחד.
- נדפיס את מספר פרקי הזמן ואת המתח.
באלגוריתם לא מופיע שום מספר סבבים, ומה שקובע את סיומו הוא התנאי. ובקוד:
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.2 תחביר בסיסי של לולאת while
הפקודה while פירושה "כל עוד". כותבים אותה כך:
while condition:
statement
statement
next_statement- אחרי התנאי באות נקודתיים
:, כמו ב-if, ובלעדיהן התוכנית אינה מתחילה. - הפקודות המוזחות הן גוף הלולאה, והן אלה שחוזרות.
- התנאי נבדק לפני כל סבב. אם הוא מתקיים, מתבצע כל הגוף וחוזרים אל התנאי. אם לא, עוברים לפקודה שאחרי הגוף.
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
| פקודה | blink | blink <= 3 | פלט |
|---|---|---|---|
| blink = 1 | 1 | ||
| while blink <= 3: | True | ||
| print("LED blink", blink) | LED blink 1 | ||
| blink += 1 | 2 | ||
| while blink <= 3: | True | ||
| print("LED blink", blink) | LED blink 2 | ||
| blink += 1 | 3 | ||
| while blink <= 3: | True | ||
| print("LED blink", blink) | LED blink 3 | ||
| blink += 1 | 4 | ||
| 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 V | while volts < 12: | הערך הראשון של volts ועדכונו |
עד שהמשתמש יכתוב -1 | while 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
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
| פקודה | adc | adc < 0 or adc > 1023 | פלט |
|---|---|---|---|
| adc = int(input("ADC (0-1023): ")) | 1500 | ADC (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): ")) | 512 | ADC (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
duty בין 0 ל-100, ואז מדפיסה אותו.4.4 קליטת כמות לא ידועה של נתונים (תבנית קלט עם זקיף)
הטכנאי רושם את ההספקים של המכשירים המחוברים לקו בזה אחר זה, ואינו יודע מראש כמה הם. איך יודיע לתוכנית שסיים? יכתוב ערך מוסכם שאינו יכול להיות נתון אמיתי.
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
| פקודה | total | watts | watts != 0 | פלט |
|---|---|---|---|---|
| total = 0 | 0 | |||
| watts = float(input("Load W (0 stops): ")) | 60.0 | Load W (0 stops): 60 | ||
| while watts != 0: | True | |||
| total += watts | 60.0 | |||
| watts = float(input("Load W (0 stops): ")) | 25.0 | Load W (0 stops): 25 | ||
| while watts != 0: | True | |||
| total += watts | 85.0 | |||
| watts = float(input("Load W (0 stops): ")) | 0.0 | Load 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, שרחוק מכל טמפרטורה שהחיישן מודד.x = int(input("x: "))
y = 0
while x != 0:
y += x * 2
x = int(input("x: "))
print(y)4.5 תרגום תנאי סיום לתנאי הלולאה
השאלה בבחינה ובחיים מתארת בדרך כלל מתי עוצרים: "עצור כשהסכום מגיע לכך וכך, או אחרי כך וכך קריאות". ואילו while צריך את מתי ממשיכים. לכן מתרגמים:
| עוצרים כאשר | ממשיכים כל עוד | מה קרה |
|---|---|---|
x == -1 | x != -1 | שוויון הופך לאי-שוויון |
total >= 100 | total < 100 | הופכים את ההשוואה, ולא שוכחים את מקרה השוויון |
v < 0 or v > 5 | v >= 0 and v <= 5 | הופכים כל השוואה, ו-or הופך ל-and |
a == 0 and b == 0 | a != 0 or b != 0 | הופכים כל השוואה, ו-and הופך ל-or |
העמודה השלישית בשתי השורות האחרונות היא מה שאנחנו מכירים ממערכות ספרתיות בשם חוקי דה-מורגן: כששוללים תנאי מורכב, שוללים כל חלק בו, ומחליפים את and ב-or ולהפך. נבדוק זאת לכל הערכים האפשריים:
| a | b | not (a or b) | not a and not b | not (a and b) | not a or not b |
|---|---|---|---|---|---|
| False | False | True | True | True | True |
| False | True | False | False | True | True |
| True | False | False | False | True | True |
| True | True | False | False | False | False |
העמודות השלישית והרביעית זהות, וכך גם החמישית והשישית.
| התנאי | |
|---|---|
| עוצרים כאשר | 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:.
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 = 5not (a > 3 or b > 3)a <= 3 and b <= 3not (a > 3 and b > 3)a <= 3 or b <= 3not a == 2not (a == 2 or b == 2)False 2 False 3 True 4 True 5 False 6 False 4.6 משימות חישוב טיפוסיות
רוב הלולאות בתוכניות עושות אחד מדברים מעטים: סופרות, מחברות, או מחפשות את הגדול ואת הקטן. נלמד כל אחד מהם כתבנית מוכנה, ואז נשלב ביניהם. בכל תבנית נשאל שתי שאלות: מה הערך הראשון לפני הלולאה, ומה קורה בכל סבב?
מונה:
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 בתוך גוף הלולאה:
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.
ההבדל בין מונה לצובר פשוט: המונה מוסיף אחד, והצובר מוסיף את הערך עצמו. ויש צובר שכופל במקום לחבר, ואז אסור לו להתחיל מאפס:
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
ערכי קיצון:
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.
שילוב בין המשימות:
-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 = 0 | count += 1 |
| מונה עם תנאי | count = 0 | if ok: count += 1 |
| צובר בחיבור | total = 0 | total += x |
| צובר בכפל | gain = 1 | gain *= x |
| הגדול | top = first | if x > top: top = x |
| הקטן | low = first | if x < low: low = x |
| ממוצע | צובר ומונה | אחרי הלולאה: total / count |
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 | מדלגת על שאר הגוף בסבב הזה, וחוזרת לבדיקת התנאי |
שתי הפקודות מופיעות כמעט תמיד בתוך if, כי קופצים רק במקרה מיוחד.
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 מקצרת את התוכנית, והתנאי המורכב מראה את סיבת היציאה בראש הלולאה. שתי הדרכים מקובלות.
-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
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 += 1volts = 12
while volts > 0:
print(volts)
volts += 3x = 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
k = 10
while k > 0:
k = k - 4k = 10
while k != 0:
k = k - 4k = 1
while k > 0:
k = k + 14.9 ניתוח נכונות בעזרת טבלת מעקב
טבלת המעקב חושפת מה קורה באמת, ולא מה שאנחנו חושבים שקורה. בלולאה קוראים אותה כך: מה ערכו של כל משתנה בבדיקת התנאי הראשונה? מה ערכו בסבב האחרון? ומה ערכו אחרי היציאה? רוב הטעויות בלולאות קורות בהתחלה או בסוף, כלומר סבב אחד יותר או פחות.
-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)| פקודה | count | total | v | v != -1 | פלט |
|---|---|---|---|---|---|
| count = 1 | 1 | ||||
| total = 0 | 0 | ||||
| v = int(input("V (-1 stops): ")) | 10 | V (-1 stops): 10 | |||
| while v != -1: | True | ||||
| total += v | 10 | ||||
| count += 1 | 2 | ||||
| v = int(input("V (-1 stops): ")) | 20 | V (-1 stops): 20 | |||
| while v != -1: | True | ||||
| total += v | 30 | ||||
| count += 1 | 3 | ||||
| v = int(input("V (-1 stops): ")) | -1 | V (-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, -1 | 15.0 | 15.0 |
| ערך אחד | 10, -1 | 10.0 | 10.0 |
| אין ערכים | -1 | הודעה | No readings |
| ערכים שווים | 7, 7, 7, -1 | 7.0 | 7.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, וזו דרך מהירה לבדיקה: לשאול אם המצב ביציאה הוא מה שרצינו.
יעילות:
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
| המספר המבוקש | הדרך הראשונה | הדרך השנייה |
|---|---|---|
10 | Found 10 in 10 steps | Found 10 in 10 steps |
731 | Found 731 in 731 steps | Found 731 in 10 steps |
1023 | Found 1023 in 1023 steps | Found 1023 in 10 steps |
אותה תוצאה, ושתי התוכניות נכונות. אבל הדרך השנייה מסתפקת תמיד בעשרה סבבים, וזה מספר הסיביות שנדרשנו לו בסעיף 4.6, ואילו הדרך הראשונה צריכה עד 1023 סבבים.
break או בתנאי מורכב. ולא שמים בגוף חישוב שאינו משתנה מסבב לסבב. למשל, בתחנה מחשבים את step = 5 / 1023 פעם אחת לפני הלולאה.4.10 קינון ושילוב מבני if, while
גוף הלולאה יכול להכיל כל פקודה, וכך גם גוף התנאי. זה הקינון. השתמשנו ב-if בתוך while לאורך כל הפרק, ואפשר גם לשים לולאה בענף של תנאי כשהיא דרושה רק במקרה מסוים. כל רמת קינון מוסיפה הזחה.
שרשרת תנאים בתוך לולאה:
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
לולאה בתוך לולאה:
כששמים לולאה בתוך לולאה, הלולאה הפנימית מתבצעת כולה בכל סבב של החיצונית. זה דומה למחוגי השעון: מחוג הדקות משלים סיבוב שלם בכל צעד של מחוג השעות.
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 += 33 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| פקודה | row | col | row <= 2 | col < row | פלט |
|---|---|---|---|---|---|
| row = 1 | 1 | ||||
| while row <= 2: | True | ||||
| col = 1 | 1 | ||||
| while col < row: | False | ||||
| row += 1 | 2 | ||||
| while row <= 2: | True | ||||
| col = 1 | |||||
| while col < row: | True | ||||
| print(row, col) | 2 1 | ||||
| col += 1 | 2 | ||||
| while col < row: | False | ||||
| row += 1 | 3 | ||||
| while row <= 2: | False |
a = 1
while a <= 3:
b = a
while b <= 3:
print(a * b, end=" ")
b += 1
print()
a += 1break יוצאת מן הלולאה הפנימית בלבד, והחיצונית ממשיכה כרגיל.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
בשני הפרקים הקודמים התחנה קראה ערך אחד והחליטה. עכשיו היא אוספת קריאות ממיר של מתח הסוללה עד הזקיף -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, אחרת הלולאה תסתובב במקום.
עיקרי הפרק:
- לולאה צריכה אתחול, תנאי ועדכון. התנאי נבדק לפני כל סבב, וייתכן שהגוף לא יתבצע כלל.
- בפייתון אין
do while. כותבים מסננת עקשנית בקריאה כפולה או בצורהwhile Trueעםbreak. - עם זקיף קוראים לפני הלולאה ובסוף הגוף, ולכן הזקיף אינו נכנס לחישוב.
- תנאי הלולאה הוא השלילה של תנאי הסיום: הופכים כל השוואה, ומחליפים את
orב-and. - מונה וצובר מתחילים מאפס, צובר כפל מאחד, וערך קיצון מן הקריאה האמיתית הראשונה.
- טבלת מעקב ומקרי קצה חושפים טעויות בלולאות, ומספר הסבבים הוא מדד פשוט ליעילות.
- בלולאות מקוננות אתחול הפנימית נמצא בתוך החיצונית, ו-
breakיוצאת מן הפנימית בלבד.
סיכום פקודות הפרק:
| פקודה | מה היא עושה |
|---|---|
| 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 6duty = int(input("Duty (0-100): "))
while duty < 0 or duty > 100:
duty = int(input("Duty (0-100): "))
print("Duty:", duty)120, 75 היא מדפיסה: Duty: 7516 , 04 16. המשתנה a הוא מונה של כל הערכים, ו-b הוא צובר של הערכים הגדולים מ-5.1 , 3 , 4 , end 5k מגיע לערך שלילי. (ב) לא מסתיימת: הערכים הם 10, 6, 2, -2 ואז שליליים, כלומר הם מדלגים על האפס ואינם שווים לו לעולם. (ג) לא מסתיימת: k גדל, ולמספר השלם בפייתון אין גבול עליון.1 2 3 / 4 6 / 9 שאלות לתרגול:
k = 2
s = 0
while k < 10:
s += k
k = k + 3
print(k, s)11 15(א) עוצרים כאשר
x == 0.(ב) עוצרים כאשר
t >= 100 or n == 20.(ג) עוצרים כאשר
a < 0 and b < 0.x != 0 , (ב) t < 100 and n != 20 , (ג) a >= 0 or b >= 0ch = 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 3n = 1 while n <= 4 print(n * 5) n =+ 1
+=. הכתיב =+ הוא השמה של המספר החיובי 1, ומוביל לביצוע אינסופי:n = 1
while n <= 4:
print(n * 5)
n += 15 , 10 , 15 , 20x = int(input("x: "))
n = 0
while x > 1:
x = x // 3
n += 1
print(x, n)| פקודה | x | n | x > 1 | פלט |
|---|---|---|---|---|
| x = int(input("x: ")) | 30 | x: 30 | ||
| n = 0 | 0 | |||
| while x > 1: | True | |||
| x = x // 3 | 10 | |||
| n += 1 | 1 | |||
| while x > 1: | True | |||
| x = x // 3 | 3 | |||
| n += 1 | 2 | |||
| while x > 1: | True | |||
| x = x // 3 | 1 | |||
| n += 1 | 3 | |||
| while x > 1: | False | |||
| print(x, n) | 1 3 |
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.0good = 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.0capacity = 100
years = 0
while capacity >= 80:
capacity = capacity * 0.97
years += 1
print("Years:", years)
print("Capacity:", round(capacity, 1))Years: 8 , Capacity: 78.4999. הדפיסו את האורך של הרצף הארוך ביותר של קריאות עוקבות מעל 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: 3a = 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: 12goal = 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-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
| סעיף | המבוקש | תשובה |
|---|---|---|
| כל השאלה | סופרים הזמנות של חולצות וסווצ׳רים עד מספר לא חוקי, ואז מדפיסים את שני המספרים ואת הסכום הכולל. | למטה |
נתחיל בתרגום: עוצרים כשהמספר שונה מ-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.
| סעיף | המבוקש | תשובה |
|---|---|---|
| כל השאלה | קוראים מספר מועמד וסכום תרומה עד שתנאי העצירה מתקיים, ואז מדפיסים את הסכום של כל מועמד, את מספר התורמים ואת התרומה הגבוהה ביותר של כל מועמד. | למטה |
תנאי הסיום מורכב: הסכום הכולל מגיע למיליון או מספר התורמים מגיע לאלף, ונתרגם אותו כמו בסעיף 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.