לולאת while
לולאת while ב-R ממשיכה להריץ את הגוף שלה כל עוד תנאי נשאר TRUE. בעוד שלולאת for רצה פעם אחת לכל איבר של משהו, while רצה מספר לא ידוע של פעמים, עד שתנאי משתנה:
שלושה חלקים גורמים לכל לולאת while לעבוד, ושלושתם נראים כאן:
- הכנה לפני הלולאה:
count <- 1נותן לתנאי משהו לבדוק. - התנאי:
count <= 5, שבנוי מאופרטורי ההשוואה הרגילים, ונבדק לפני כל מעבר. - העדכון בתוך הגוף:
count <- count + 1מקדם את הלולאה לעבר עצירה.
מחקו את חלק 3 והתנאי יהיה TRUE לנצח: הלולאה לא תיגמר לעולם. זה הסיכון המגדיר של while, ושאר העמוד עוסק בעיקר בניהול שלו.
התנאי נבדק לפני כל מעבר
while בודקת את התנאי שלה קודם, ואז מריצה את הגוף. יש לזה שתי השלכות. אם התנאי כבר FALSE בהתחלה, הגוף רץ אפס פעמים:
והבדיקה קורית בין מעברים מלאים, לא באמצע הגוף: ברגע שמעבר מתחיל, הוא רץ עד הסוף גם אם התנאי הופך ל-FALSE באמצע. אם צריך לצאת באמצע הגוף, בשביל זה יש break (בהמשך).
דוגמה מלאה: חציה עד שיורדים מתחת לסף
הבית הטבעי של while הוא "להמשיך עד ש": אי אפשר לדעת מראש כמה מעברים זה ייקח, ולכן for לא מתאימה. כמה פעמים אפשר לחצות את 1000 לפני שהוא יורד מתחת ל-1?
עשר חציות, שמסתיימות בערך ב-0.977. הדפוס, משתנה מצב שהגוף מקטין ומונה שהוא מגדיל, מכסה חלק עצום מלולאות while אמיתיות: שיטות נומריות שרצות עד שהשגיאה קטנה מספיק, סימולציות שרצות עד שמשאב נגמר, ניסיונות חוזרים שממשיכים עד שקריאה מצליחה.
repeat יחד עם break: ה-do-while של R
אין ב-R מילת מפתח do-while, אין לולאה שבודקת את התנאי שלה אחרי הגוף. אבל יש repeat, שחוזרת בלי תנאי לנצח, ו-break, שיוצאת מכל לולאה מיד. שימו את בדיקת ה-break בסוף גוף של repeat ובניתם בדיוק do-while: לולאה שמובטח שהגוף שלה ירוץ לפחות פעם אחת:
הגוף תמיד רץ לפני הבדיקה הראשונה, כך ש-n מוכפל לפחות פעם אחת; הלולאה יוצאת ב-128, ההכפלה הראשונה שעוברת את 100. השתמשו בצורה הזאת כש"עושים את הדבר, ואז מחליטים אם לעשות אותו שוב" הוא התיאור הכן: קריאת קלט עד שהוא תקין, הגרלת דגימות עד שאחת מתאימה. repeat בלי break שאפשר להגיע אליו היא לולאה אינסופית מעצם הגדרתה, אז כתבו קודם את ה-break, ורק אחר כך מלאו את הגוף.
break עובדת אותו דבר בלולאות while ו-for: זו היציאה הכללית של "סיימנו כאן", והיא מתאימה באופן טבעי ל-משפטי if שבודקים תנאי באמצע הגוף.
next בתוך לולאת while
next מדלגת על שאר המעבר הנוכחי וקופצת חזרה לבדיקת התנאי. היא עובדת ב-while בדיוק כמו ב-for, עם מלכודת אחת שייחודית ל-while: אם שלב העדכון בא אחרי ה-next, הדילוג עליו אומר שמשתנה הלולאה לעולם לא מתקדם, ובניתם לולאה אינסופית. שימו את העדכון לפני ה-next:
זה מדפיס את המספרים הזוגיים מ-2 עד 10. הזיזו את i <- i + 1 אל מתחת לשורת ה-next, והערך האי-זוגי הראשון ידלג על העדכון לנצח. בלולאות for הסכנה הזאת לא קיימת (משתנה הלולאה מתקדם אוטומטית), וזו עוד סיבה להעדיף for כשפשוט עוברים על סדרה.
הגנה מפני לולאות אינסופיות
כל לולאה אינסופית היא אותו באג: שום דבר בגוף לא משנה את המשתנים שהתנאי תלוי בהם, או שהוא משנה אותם בכיוון הלא נכון. בעבודה אינטראקטיבית אפשר לקטוע עם Esc או Ctrl+C, אבל בסקריפט התהליך פשוט נתקע. שני הרגלים מונעים את זה.
ראשית, כשכותבים את התנאי, שאלו מיד "איזו שורה בגוף מקדמת את זה לעבר FALSE?" וודאו שהשורה הזאת רצה בכל מסלול בגוף (שימו לב למלכודת ה-next שלמעלה).
שנית, בלולאות שקשה להוכיח שהן מסתיימות, הוסיפו שומר של מספר איטרציות מרבי, כך שבאג לוגי יפיק תוצאה שאפשר לאבחן במקום סשן קפוא. כאן הוא מגן על לולאה בסגנון Collatz: חוצים שוב ושוב מספרים זוגיים והופכים x אי-זוגי ל-3 * x + 1, עד שמגיעים ל-1:
כשמתחילים מ-10, הלולאה מגיעה ל-1 ב-6 צעדים. אם ערך התחלתי כלשהו לא היה מגיע ל-1 לעולם, הסעיף steps < 10000 עדיין היה מסיים את הלולאה, והמצב המודפס היה אומר לכם שמשהו השתבש: תשובה שגויה שאפשר לראות עדיפה על תוכנית שלעולם לא עונה. && עם הקיצור שלו הוא האופרטור הנכון כאן: שני הצדדים הם ערכים בודדים, והשומר נקרא כמשפט אחד.
while מול for: איך בוחרים
כלל האצבע הוא לגבי מה שיודעים מראש:
- יודעים על מה עוברים (איברים של וקטור, מספרים מ-1 עד n): השתמשו ב-לולאת for, משתנה הלולאה מנהל את עצמו.
- יודעים רק את תנאי העצירה (עד התכנסות, עד מתחת לסף, עד שהקלט תקין): השתמשו ב-
while. - הגוף חייב לרוץ לפחות פעם אחת לפני שבדיקה כלשהי הגיונית: השתמשו ב-
repeatעםbreakשמסיים.
אם אתם מוצאים את עצמכם מתחזקים מונה ידני בלולאת while רק כדי לעבור על וקטור, זו לולאת for בתחפושת: החליפו.
מה לקחת מכאן
while (condition) { ... }בודקת קודם, ואז רצה: תנאי שקרי בהתחלה פירושו אפס מעברים.- הגוף חייב לקדם את התנאי לעבר
FALSE; השלישייה של הכנה, תנאי ועדכון היא כל המשמעת. - אין ב-R מילת מפתח do-while:
repeat { body; if (done) break }היא הדרך המקובלת, ומבטיחה לפחות מעבר אחד. breakיוצאת מכל לולאה; עםnextבתוךwhile, עדכנו את משתנה הלולאה לפני הדילוג, אחרת הוא לא יתקדם לעולם.- שומר של מספר איטרציות מרבי (
&& steps < 10000) הופך תקיעה אפשרית לתשובה שגויה גלויה שאפשר לדבג.
הבא בתור: וקטורים. מבנה הנתונים שכל לולאה בעמוד הזה עברה עליו, והסיבה שהרבה לולאות ב-R יכולות להיעלם לגמרי.
שאלות נפוצות
איך לולאת while עובדת ב-R?
while (condition) { ... } בודקת קודם את התנאי, ומריצה את הגוף רק כל עוד הוא נשאר TRUE. משהו בגוף חייב בסוף להפוך את התנאי ל-FALSE, בדרך כלל עדכון של מונה או הקטנה של ערך, אחרת הלולאה לא נגמרת לעולם. אם התנאי הוא FALSE כבר בהתחלה, הגוף רץ אפס פעמים.
האם יש ב-R לולאת do-while?
לא כמילת מפתח. הדרך המקובלת היא repeat { ...body...; if (condition) break }: repeat חוזרת בלי תנאי, כך שמובטח שהגוף ירוץ לפחות פעם אחת, וה-break בסוף ממלא את התפקיד של בדיקת היציאה של do-while.
איך עוצרים לולאה אינסופית ב-R?
בעבודה אינטראקטיבית, הקישו Esc (ב-RStudio) או Ctrl+C (בטרמינל) כדי לקטוע. כדי למנוע אותה מלכתחילה: ודאו שהגוף מעדכן בכל מעבר את המשתנים שבתנאי, והוסיפו שומר של מספר איטרציות מרבי, while (working && steps < 10000), כך שבאג לוגי יפיק ספירה שגויה במקום סשן קפוא.