לולאות for לעומת לולאות while#
ביחידה הזו הכרנו את הסוג השני של לולאות בשפת פייתון – לולאות while. בואו נסכם מה ראינו עד עכשיו תוך השוואה בין שני סוגי הלולאות.
לולאות
for: משמשות לביצוע קטע קוד באופן חזרתי, מספר ידוע מראש של פעמים. לולאות אלו שימושיות למשל לצורך מעבר על תוי מחרוזת (str), אברי רשימה (list), או מספרים בטווח (range). בכל המקרים האלו אכן מספר האיטרציות ידוע מראש. כמו כן, אם פשוט רוצים לבצע פעולה כלשהי מספר פעמים, ניתן לשים את הפעולה בתוך לולאת for מהצורה:
for i in range(n):
do_something
כאשר n הוא מספר הפעמים שרוצים לבצע את הפעולה.
לולאות
while: משמשות לביצוע קטע קוד באופן חזרתי כל עוד מתקיים תנאי מסויים. לולאות אלו הכרחיות כאשר לא ידוע לנו מראש מספר האיטרציות, כפי שראינו בדוגמת הטלת הקוביה עד להשגת התוצאה 6. יחד עם זאת, אפשר כמובן להשתמש בלולאותwhileגם כאשר ידוע מראש מספר האיטרציות. תמיד אפשר לכתוב למשל לולאה כזו:
i=0
while i<n:
do_something
i = i+1
לולאה זו למעשה שקולה ללולאת ה-for שלעיל, וגם היא מבצעת פעולה מסויימת בדיוק n פעמים.
לולאות while בפייתון דורשות זהירות מסויימת, כי המתכנת אחראי להגדרת הלולאה באופן תקין, שתעצור אחרי מספר סופי של איטרציות (כלומר שלא תיכנס ללולאה אינסופית). הטעות הנפוצה בעניין זה היא לשכוח לקדם את הלולאה אל עבר סיומה. בדוגמה הנ”ל זוהי הפקודה i = i+1 שדואגת שאכן הלולאה תסתיים, כי בכל פעם i מתקרב ל-n (בהנחה ש-n לא מספר שלילי!). אבל לולאה אינסופית יכולה להיגרם גם מטעות בתנאי הלולאה או מאיתחול לא נכון של משתנה הלולאה (i בדוגמה הנ”ל).
בחלק הבא נראה עוד שתי דוגמאות מעניינות לשימוש בלולאות (דוגמה אחת לכל סוג לולאה), ונקדיש עוד קצת זמן לתרגול הנושא החשוב הזה.
זיהוי פלינדרום#
זוכרים מהו פלינדרום?
את השפה העברית אנחנו כותבים וקוראים בד”כ מימין לשמאל. כך גם בערבית. לעומת זאת בשפות כמו אנגלית, רוסית, צרפתית וגם באמהרית כותבים וקוראים משמאל לימין. בכל השפות האלו ובאחרות ישנן מילים שאפשר לקרוא אותן בשני הכיוונים באותו אופן. למשל, המילה ‘אמא’ בעברית, או המילה radar באנגלית. מילים כאלו נקראות “פלינדרום”.
כעת ננסח בצורה מעט יותר פורמלית את בעית הפלינדרום. הקלט לבעיה שלנו הוא מחרוזת. אם המחרוזת ניתנת לקריאה בשני הכיוונים באופן זהה הפלט יהיה True.
ראינו בשיעור על מחרוזות פתרון לבעית הפלנידרום. כעת נבחן גישה לפתרון הבעיה באמצעות רשימות.
הנה אלגוריתם אפשרי: נתחיל משני קצוות המחרוזת ונשווה את התווים המתאימים. אם הם שונים, נוכל כבר ברגע זה להכריז שהמחרוזת היא לא פלינדרום. אם הם שווים, נזוז צעד אחד פנימה משני צדי המחרוזת ונבדוק אם שני התווים הבאים שווים.
למשל, “RADAT” תתגלה כמחרוזת שאיננה פלינדרום כבר בבדיקת זוג התווים הראשון שבקצוות.
לעומת זאת אם התווים שבקצוות שווים, לא נוכל לדעת דבר בשלב זה. המחרוזת יכולה להיות פלינדרום ויכולה שלא להיות, צריך להמשיך לבדוק. לכן, נזוז צעד אחד פנימה משני קצוות המחרוזת ונבדוק אם שני התווים הבאים שווים. ושוב: אם הם לא שווים, נוכל לסיים ולהכריז שהמחרוזת היא לא פלינדרום.
אחרת נצטרך להמשיך, וחוזר חלילה.
מתי נוכל לקבוע בוודאות שמצאנו פלינדרום? התהליך הזה יימשך כל עוד התו הימני שמשווים אכן נמצא מימין לתו השמאלי שמשווים. בפעם הראשונה שתנאי זה לא מתקיים, נוכל להכריז שהמחרוזת הנתונה היא אכן פלינדרום. שימו לב שלא צריך להמשיך לבצע את הבדיקה אחרי שמתקיים התנאי, כי ברגע שנעבור את האמצע נתחיל לבצע השוואות שכבר ביצענו קודם.
רגע, אמרנו כל עוד?? אמרנו לולאת while!
הרבה פעמים הדרך שבה אנחנו מנסחים לעצמנו את האלגוריתם במילים מעידה על סוג הלולאה שאותה כדאי לבחור. במקרה הזה טבעי להשתמש בלולאות while, אם כי היה אפשר גם להשתמש בלולאות for.
לפני שנקפוץ לממש את האלגוריתם הזה בפייתון, בואו נכתוב אותו בפסאודו קוד. נשתמש בשני משתנים שיכילו אינדקסים במחרוזת. הם יאותחלו לקצוות, ויזוזו פנימה בכל איטרציה.
זיהוי פלינדרום (קלט: המחרוזת st)
אתחל
l=0, r=len(st)-1כל עוד
l<r:
2.1 אםst[l]לא שווה ל-st[r]:
2.1.1. החזרFalseוסיים
2.2 הגדל אתlב- 1 והקטן אתrב- 1החזר
True
בהתחלה נגדיר שני משתנים עם השמות l (left) ו-r (right) . נאתחל את l לאינדקס השמאלי ביותר של המחרוזת, כלומר 0, ואת r לימני ביותר, כלומר אורך המחרוזת פחות 1 (פחות 1, כי מתחילים ב-0). ואז נריץ בלולאה כל עוד l<r, ונשווה את תווי המחרוזת באינדקסים l ו-r.
אם הם שונים אנחנו יודעים בדיוק מה לעשות – לסיים את ריצת האלגוריתם ולהחזיר False.
אם הם שווים, צריך להמשיך בלולאה, אבל לא לפני שנזיז את l מקום אחד ימינה, כלומר נגדיל אותו ב-1, ואת r נזיז שמאלה, כלומר נקטין אותו ב-1. משורה 2.2 חוזרים לבדוק את התנאי שבשורה 2 פעם נוספת.
כך ממשיך התהליך עד שקורה אחד משניים: או שמתישהו מגלים תווים מתאימים ששונים זה מזה, ומחזירים False, או שהלולאה מסתיימת כי האינדקס l כבר לא קטן מהאינדקס r, כלומר או שהמשתנים l ו-r נפגשו, או שהם חלפו זה על פני זה. אחד מהשניים בטוח יקרה בשלב מוקדם או מאוחר. אם הלולאה הסתיימה מהסיבה השניה, אפשר סוף סוף להחזיר True.
שימו לב: ניתן לכתוב פונקציה אשר תעבוד עבור מחרוזות ורשימות, בלי שינוי בקוד.
בתרגיל זה נכתוב שתי פונקציות - אחת לזיהוי פלינדרום מושלם ואחת לזיהוי פלינדרום “כללי”.
בואו נתחיל מזיהוי פלינדרום מושלם - הפונקציה שנכתוב נקראת is_palindrome ומקבלת כקלט מחרוזת st, להלן תזכורת לפסאודו-קוד שתארנו כעת:
זיהוי פלינדרום (קלט: המחרוזת st)
אתחל
l=0, r=len(st)-1כל עוד
l<r:
2.1 אםst[l]לא שווה ל-st[r]:
2.1.1. החזרFalseוסיים
2.2 הגדל אתlב- 1 והקטן אתrב- 1החזר
True
השלימו את הקוד לפונקציה. הקפידו להשתמש בלולאת while ולא בלולאת for. כמו כן, הריצו את הפונקציה שלכם על דוגמאות ההרצה הבאות כדי לוודא שהיא עובדת נכון (לפחות עבור מקרים אלו).
דוגמאות הרצה:
print(is_palindrome('anna'))
>>> True
print(is_palindrome('radar radar'))
>>> True
print(is_palindrome('Anna'))
>>> False
def is_palindrome(st):
# delete pass and fill in your code below
pass
### TESTS ###
print("********************")
print("Starting the test:")
print("********************")
print("Checking if 'anna' is a perfect palindrome")
ans = is_palindrome('anna')
if ans == True:
print("CORRECT: 'anna' is a perfect palindrome")
else:
print("WRONG: 'anna' is a perfect palindrome but the code returned", ans)
print("********************")
print("Checking if 'A man, a plan, a canal, Panama' is a perfect palindrome")
ans = is_palindrome('A man, a plan, a canal, Panama')
if ans == False:
print("CORRECT: 'A man, a plan, a canal, Panama' is not a perfect palindrome")
else:
print("WRONG: 'A man, a plan, a canal, Panama' is not a perfect palindrome but the code returned", ans)
print("********************")
print("Checking if 'a nN.a' is a perfect palindrome")
ans = is_palindrome('a nN.a')
if ans == False:
print("CORRECT: 'a nN.a' is not a perfect palindrome")
else:
print("WRONG: 'a nN.a' is not a perfect palindrome but the code returned", ans)
********************
Starting the test:
********************
Checking if 'anna' is a perfect palindrome
WRONG: 'anna' is a perfect palindrome but the code returned None
********************
Checking if 'A man, a plan, a canal, Panama' is a perfect palindrome
WRONG: 'A man, a plan, a canal, Panama' is not a perfect palindrome but the code returned None
********************
Checking if 'a nN.a' is a perfect palindrome
WRONG: 'a nN.a' is not a perfect palindrome but the code returned None
כעת, נכתוב את הפונקציה is_general_palindrome, אשר מזהה פלינדרום גם אם אינו מושלם.
למה הכוונה? בואו נסתכל על המשפט: A man, a plan, a canal, Panama.
משפט זה הוא פלינדרום אך ורק אם מתעלמים מהבדלים של רווחים, סימני פיסוק ושימוש באותיות גדולות או קטנות. נרצה כעת לכתוב פונקציה שתוכל לזהות גם את הפלינדרום הזה.
למה זה טוב? לפעמים אין לנו שליטה מלאה על הקלט של התוכניות אותן אנו כותבים. מאחר שברצוננו שהתוכנית שלנו תוכל לפעול על מגוון רחב ככל האפשר של קלטים, נרצה לעיתים לבצע התאמות לקלט שלנו לפני הרצת התוכנית.
השתמשו בכלים שלמדתם בפרק מחרוזות על מנת “לנקות” את המחרוזת!
def is_palindrome(st):
# You can use this function if you want
pass
def is_general_palindrome(st):
# delete pass and fill in your code below
pass
### TESTS ###
print("********************")
print("Starting the test:")
print("********************")
print("Checking if 'anna' is a general palindrome")
ans = is_general_palindrome('anna')
if ans == True:
print("CORRECT: 'anna' is a general palindrome")
else:
print("WRONG: 'anna' is a general palindrome but the code returned", ans)
print("********************")
print("Checking if 'A man, a plan, a canal, Panama' is a general palindrome")
ans = is_general_palindrome('A man, a plan, a canal, Panama')
if ans == True:
print("CORRECT: 'A man, a plan, a canal, Panama' is a general palindrome")
else:
print("WRONG: 'A man, a plan, a canal, Panama' is a general palindrome but the code returned", ans)
print("********************")
print("Checking if 'a nN.a' is a general palindrome")
ans = is_general_palindrome('a nN.a')
if ans == True:
print("CORRECT: 'a nN.a' is a general palindrome")
else:
print("WRONG: 'a nN.a' is a general palindrome but the code returned", ans)
print("********************")
print("Checking if 'radat' is a general palindrome")
ans = is_general_palindrome('radat')
if ans == False:
print("CORRECT: 'radat' is not a general palindrome")
else:
print("WRONG: 'radat' is not a general palindrome but the code returned", ans)
********************
Starting the test:
********************
Checking if 'anna' is a general palindrome
WRONG: 'anna' is a general palindrome but the code returned None
********************
Checking if 'A man, a plan, a canal, Panama' is a general palindrome
WRONG: 'A man, a plan, a canal, Panama' is a general palindrome but the code returned None
********************
Checking if 'a nN.a' is a general palindrome
WRONG: 'a nN.a' is a general palindrome but the code returned None
********************
Checking if 'radat' is a general palindrome
WRONG: 'radat' is not a general palindrome but the code returned None
חישוב עצרת (n!)#
הגדרת עצרת: עצרת של מספר n (מסומנת במתמטיקה באמצעות סימן קריאה) היא מכפלת כל המספרים החיוביים הקטנים או שווים לו.
דוגמאות:
\(1\cdot2\cdot3=3!\)
\(1\cdot2\cdot3\cdot4\cdot5\cdot6=6!\)
\(1\cdot2\cdot\dots\cdot(𝑛−1) \cdot 𝑛=𝑛!\)
בשלב זה אתם תממשו את פונקציית העצרת באמצעות שני סוגי הלולאות - while וfor.
מימוש עצרת עם לולאת while#
def while_factorial(n):
pass
print("5!+3!+6! =", while_factorial(5) + while_factorial(3) + while_factorial(6))
לחצו כאן כדי לצפות בפתרון
def while_factorial(n):
fact = 1
i = 1
while i <= n:
fact = fact * i
i = i + 1
return fact
מימוש עצרת עם לולאת for#
def for_factorial(n):
pass
print("5!+3!+6! =", for_factorial(5) + for_factorial(3) + for_factorial(6))
לחצו כאן כדי לצפות בפתרון
def for_factorial(n):
for i in range(2, n+1):
fact = fact * i
return fact