זרימת בקרה

last#

יציאה מיידית מלולאה, תוך דילוג על שאר הגוף ועל בלוק ה־continue.

last הוא המקבילה של Perl ל־break של C. הוא מעביר את השליטה אל מחוץ ללולאה העוטפת אל ההוראה הבאה אחריה. ההוראות הנותרות באיטרציה הנוכחית אינן רצות, וגם לא בלוק ה־continue של הלולאה - זהו ההבדל המהותי מ־next. ללא ארגומנט הוא מכוון ל־while, until, for, foreach או בלוק ריק העוטפים הפנימיים ביותר; עם LABEL הוא מכוון ללולאה מתויגת מעבר לכך.

תקציר#

last
last LABEL
last EXPR       # LABEL computed at runtime, since 5.18

מה מקבלים בחזרה#

כלום. last מבצע בקרת זרימה; הוא אינו מייצר ערך. לא ניתן להשתמש בו כדי להחזיר תוצאה מבלוק שבדרך־כלל מניב כזו, כמו eval { ... }, sub { ... } או do { ... }. בהקשרים אלה הקונסטרוקט אינו מייצר ערך, או שהתוכנית מתה עם Can't "last" outside a loop block - ראו מקרי קצה להלן.

כיוון ללולאה לפי תווית#

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

while (my $line = <$fh>) {
    last if $line =~ /^__END__$/;
    process($line);
}

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

OUTER: for my $row (@grid) {
    for my $cell (@$row) {
        last OUTER if $cell eq 'STOP';
    }
}
# control resumes here

last EXPR (Perl 5.18+) מחשב את התווית בזמן ריצה. EXPR חייב להעריך למחרוזת שמציינת תווית עוטפת:

my $target = 'OUTER';
last $target;

אינטראקציה עם בלוקי continue#

בלוק ה־continue של לולאה רץ בדרך־כלל בסוף כל איטרציה, כולל האחרונה. last מדלג עליו - זהו ההבדל המגדיר מ־next:

my $i = 0;
while ($i < 10) {
    last if $i == 3;
    print "body $i\n";
} continue {
    $i++;                 # runs after every iteration EXCEPT
                          # the one terminated by last
}
# prints body 0, body 1, body 2; then exits the loop.
# $i stays at 3 because continue did not fire the final time.

אם רוצים ש־continue ירוץ, יש להשתמש ב־next עם שומר במקום, או לקדם את המונה לפני ה־last.

התנהגות בלולאות מקוננות#

last עוזב רק לולאה אחת בכל פעם - הפנימית ביותר, אלא אם תויגה. בדוגמה זו ה־last הפנימי יוצא מה־for אבל ה־while ממשיך לרוץ:

while (1) {
    for my $x (1 .. 5) {
        last if $x == 3;
    }
    # control lands here every iteration of the while
    last;                 # needed to exit the while as well
}

כדי לשחרר מספר רמות בבת־אחת, יש לתייג את הלולאה החיצונית ולכוון אליה:

SEARCH: for my $file (@files) {
    open my $fh, '<', $file or next;
    while (my $line = <$fh>) {
        if ($line =~ /$pattern/) {
            $found = $file;
            last SEARCH;  # exits both the while and the for
        }
    }
}

בלוקים ריקים נחשבים ללולאות#

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

{
    last if $cached;
    recompute();
    store_in_cache();
}
# control continues here

זוהי הצורה היחידה של last שאינה כרוכה בקונסטרוקט מאיטר.

דוגמאות#

הפסקת קריאת קובץ בשורה ריקה - האידיום הקלאסי של סיומת־כותרת:

LINE: while (<$fh>) {
    last LINE if /^$/;
    push @header, $_;
}

מצא־ודווח על־פני מבנה דו־ממדי:

my $hit;
ROW: for my $r (0 .. $#grid) {
    for my $c (0 .. $#{ $grid[$r] }) {
        if ($grid[$r][$c] eq $needle) {
            $hit = [$r, $c];
            last ROW;
        }
    }
}

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

my $result;
{
    $result = $cache{$key}, last if exists $cache{$key};
    $result = compute($key);
    $cache{$key} = $result;
}

תווית מחושבת (Perl 5.18+). נדרשת לעיתים נדירות, אך שימושית כשלולאת היעד נבחרת דינמית:

my $level = $deep ? 'OUTER' : 'INNER';
OUTER: for (...) {
    INNER: for (...) {
        last $level if $done;
    }
}

מקרי קצה#

  • אסור בתוך grep ו־map. שניהם מצפים שהבלוק שלהם יפיק ערך עבור כל איבר קלט; last שולל זאת. המפענח מקבל את הקוד (הבלוק אינו לולאה לקסיקלית שהוא יכול לראות דרכה), אך הרצתו מתה עם Can't "last" outside a loop block. יש לסנן עם תנאי בתוך הבלוק במקום, או לעבור ללולאת for מפורשת.

  • do { ... } אינו לולאה. last בתוך do { ... } אינו מסיים את ה־do; הוא משחרר עד הלולאה האמיתית העוטפת הקרובה ביותר. אם אין כזו, התוכנית מתה עם Can't "last" outside a loop block. ליציאה מוקדמת מ־do המשמש כביטוי, ראו את העקיפות ב־perlsyn תחת ”Statement Modifiers“ - בדרך־כלל בלוק ריק עם last, או לולאת for חד־פעמית.

  • sub { ... } אינו לולאה. last בתוך גוף תת־שגרה אינו חוזר מתת־השגרה; הוא משחרר מעבר לתת־השגרה אל הלולאה העוטפת ב־caller. יש להשתמש ב־return כדי לעזוב תת־שגרה.

  • last בתוך eval אינו בורח מה־eval. eval { ... } כשלעצמו אינו לולאה, ולכן last מסתכל החוצה מעבר ל־eval אחר הלולאה העוטפת הקרובה ביותר ומשחרר אליה. ה־eval נסגר כתוצר־לוואי של אותו שחרור, אך $@ נשאר ריק - לא הועלתה חריגה. אם ל־eval אין לולאה עוטפת מעליו, התוכנית מתה עם Can't "last" outside a loop block. כדי לעזוב eval בצורה רגילה, יש ליפול אל הסוף או להשתמש ב־die (נתפס על־ידי ה־eval עצמו) או ב־return (אם ה־eval בתוך תת־שגרה ורוצים לעזוב את תת־השגרה).

  • קדימות. בניגוד לרוב האופרטורים בעלי השם, ל־last יש קדימות זהה לזו של השמה והוא פטור מכלל looks-like-a-function. לכן last ("foo") . "bar" מעביר את "foo" . "bar" כתווית, לא רק "foo". יש להשתמש ברווח, או להשמיט את הסוגריים, כדי להימנע מהפתעות:

    last LABEL;
    last "LABEL";
    
  • תווית לא־ידועה. last NOPE כאשר אין לולאה עוטפת המתויגת NOPE מתה עם Label not found for "last NOPE". זוהי שגיאת זמן־ריצה, לא שגיאת זמן־הידור - התווית נפתרת מול מחסנית הקריאות הדינמית.

  • צורת modifier של לולאה אין בלוק לצאת ממנו. EXPR if COND וצורות דומות של statement-modifier אינן לולאות; last בתוך הביטוי משחרר מעברן.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • next - מדלג לאיטרציה הבאה, מריץ קודם את בלוק ה־continue; המקבילה ל־last ששומרת את הלולאה חיה

  • redo - מפעיל מחדש את האיטרציה הנוכחית ללא בדיקת התנאי מחדש וללא הרצת continue

  • return - עוזב תת־שגרה עם ערך; הכלי הנכון כשמושיטים יד ל־last בתוך sub { ... }

  • continue - בלוק ה־loop-trailer ש־last מדלג עליו ו־next מכבד; ראו את עמודו לאיור התלת־צדדי של last / next / redo