redo#
אתחול מחדש של האיטרציה הנוכחית של לולאה מבלי לבדוק שוב את התנאי ומבלי להריץ את הבלוק continue.
redo קופץ חזרה לראש גוף הלולאה ומריץ אותו שוב על אותו קלט - התנאי השומר על הלולאה אינו מוערך שוב, $_ אינו מתקדם על־ידי while (<FH>), האיטרטור של foreach אינו מתקדם, וכל בלוק continue { } המצורף ללולאה מדולג. המעבר הנוכחי מתרחש שוב מההתחלה.
תקציר#
redo
redo LABEL
redo EXPR
מה מקבלים בחזרה#
redo אינו מחזיר ערך. הוא אופרטור בקרת־זרימה: הוא מעביר בקרה ולעולם אינו נופל להוראה הבאה. אל תנסו להשתמש בו כערך של ביטוי - בפרט, לא ניתן להשתמש בו כדי להחזיר ערך מ־eval { }, sub { }, או do { }.
לאיזו לולאה הוא מכוון#
ללא ארגומנט, redo מתייחס ללולאה העוטפת הפנימית ביותר. עם LABEL, הוא מאתחל מחדש את הלולאה המתויגת בתווית זו, ומאפשר להגיע מחוץ ללולאות מקוננות:
OUTER: while (...) {
while (...) {
redo OUTER; # restart the while (...) above
}
}
redo EXPR (התווסף ב־Perl 5.18) מחשב את התווית בזמן ריצה. הביטוי חייב להעריך למחרוזת המציינת תווית בתחום הנוכחי:
my $target = "OUTER";
redo $target;
בלוק חשוף נספר כלולאה שרצה פעם אחת, כך ש־redo בתוך בלוק הופך אותו למבנה חוזר על עצמו:
{
my $line = <STDIN>;
redo if $line =~ /^\s*$/; # skip blank lines, re-read
print $line;
}
redo מול next מול last#
כל השלושה מקבלים LABEL אופציונלי וכל השלושה משפיעים על בקרת לולאה, אך הם נבדלים במה שהם מדלגים עליו:
next- מעבר לאיטרציה הבאה. בלוקcontinueכן רץ, ואז תנאי הלולאה נבדק שוב.redo- הרצה מחדש של אותה איטרציה. בלוקcontinueאינו רץ, ותנאי הלולאה אינו נבדק שוב.
ההבדל בנוגע לבלוק continue הוא זה שמכשיל אנשים. אם לולאה משתמשת ב־continue { } להגדלת מונה או קידום סמן, redo לא יקדם אותו - זוהי כל המטרה, אך זה גם כל ה־foot-gun.
דוגמאות#
בקשה מחדש עד שהמשתמש נותן קלט תקף. בלוק ה־continue מדולג, כך ש־$attempts סופר רק איטרציות שהושלמו, וזה בדרך כלל מה שרוצים:
my $attempts = 0;
while (1) {
print "Enter a positive integer: ";
my $n = <STDIN>;
chomp $n;
redo unless $n =~ /^[0-9]+$/ && $n > 0;
print "Got $n after $attempts rejected tries.\n";
last;
} continue {
$attempts++;
}
אתחול מחדש של עיבוד השורה הנוכחית לאחר טלאי שלה - תבנית הקלאסי של ״לשקר לעצמכם לגבי מה שהרגע נקרא״, כאן הסרת הערות־בלוק בסגנון Pascal שעלולות להשתרע על מספר שורות קלט:
LINE: while (<STDIN>) {
while (s|({.*}.*){.*}|$1 |) {}
s|{.*}| |;
if (s|{.*| |) {
my $front = $_;
while (<STDIN>) {
if (/}/) { # end of comment?
s|^|$front\{|;
redo LINE; # re-process the patched line
}
}
}
print;
}
כיוון ללולאה חיצונית על־פי תווית. redo INNER יאתחל את הלולאה הפנימית על ה־$x הנוכחי; redo OUTER מאתחל את הלולאה החיצונית על ה־$x הנוכחי מבלי לקדם את האיטרטור של foreach:
OUTER: foreach my $x (@xs) {
INNER: foreach my $y (@ys) {
redo OUTER if should_restart($x, $y);
}
}
הפיכת בלוק חשוף ללולאת ניסיון־חוזר. ללא redo בלוק זה רץ בדיוק פעם אחת; עם redo הוא רץ עד שהשליפה מצליחה:
my $tries = 0;
{
my $ok = try_fetch();
if (!$ok && ++$tries < 3) {
sleep 1;
redo;
}
}
מקרי קצה#
continueאינו רץ. זהו ההבדל המגדיר מ־next. אם הלולאה מסתמכת עלcontinue { }למונים, סמנים, או ניקוי,redoיעקוף את כל זה. או שתעבירו את העבודה לתוך הגוף או שתשתמשו ב־nextבמקום.תנאי הלולאה אינו נבדק שוב.
while (cond) { ... redo; }ימשיך ללולל גם אםcondהיה כעת שקר. אתם אחראים להבטיח שמסלול ה־redoיוצא בסופו של דבר דרךlast,return, או נפילה מותנית.לולאה אינסופית בקלות.
redoללא שינוי מצב הוא לולאה אינסופית הדוקה - בדרך כלל לא מה שרוצים. תמיד שלבו אתredoעם תנאי שבסופו של דבר הופך לשקר, או עם מונה ניסיון־חוזר שמפעילlast.foreachאינו מתקדם.foreach my $x (@xs) { redo }שומר את$xמקובע על האלמנט הנוכחי ומלולל עליו לנצח. האיטרטור מתקדם רק בנפילה או ב־next.while (<FH>)אינו קורא את השורה הבאה.$_שומר את השורה הנוכחית לרוחב ה־redo, וזו בדיוק הסיבה שמסיר הערות Pascal לעיל עובד.redoאל מחוץ ל־grep/mapאינו נתמך. upstream מתעד זאת כלא־מוגדר; אל תשתמשו ב־redoליציאה או אתחול מחדש שלgrep BLOCK LISTאוmap BLOCK LIST.redoאל מחוץ ל־eval,sub, אוdoאינו return. הוא מבצע בקרת זרימה; אין ערך להחזיר.redoשיצטרך לחצות גבול כזה כדי להגיע ללולאה הוא שגיאת זמן ריצה (״Can’tredooutside a loop block״).מוזרות קדימות. בניגוד לרוב האופרטורים הנקובים בשם, ל־
redoיש קדימות של הצבה והוא פטור מכלל ה־״נראה־כמו־פונקציה״.redo ("foo")."bar"מעביר את כל השרשור כארגומנט ל־redo- הסוגריים אינם מבודדים את"foo".
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.