Ροή ελέγχου

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 και τα τρία επηρεάζουν τον έλεγχο βρόχου, αλλά διαφέρουν σε αυτό που παρακάμπτουν:

  • last - έξοδος από τον βρόχο τελείως. Το μπλοκ continue δεν εκτελείται.

  • next - προχώρα στην επόμενη επανάληψη. Το μπλοκ continue εκτελείται, μετά ελέγχεται ξανά η συνθήκη του βρόχου.

  • redo - επανάληψη της ίδιας επανάληψης. Το μπλοκ continue δεν εκτελείται, και η συνθήκη του βρόχου δεν ελέγχεται ξανά.

Η διαφορά στο μπλοκ continue είναι αυτή που πιάνει απροετοίμαστους τους χρήστες. Αν ένας βρόχος χρησιμοποιεί continue { } για αύξηση μετρητή ή προώθηση δρομέα, το redo δεν θα τον προωθήσει - αυτό είναι όλο το νόημα, αλλά είναι επίσης και όλη η παγίδα.

Παραδείγματα#

Επανειλημμένη ερώτηση μέχρι ο χρήστης να δώσει έγκυρη είσοδο. Το μπλοκ 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++;
}

Επανεκκίνηση της επεξεργασίας της τρέχουσας γραμμής αφού την μπαλώσετε - το κλασικό μοτίβο «πες ψέματα στον εαυτό σου για το τι μόλις διαβάστηκε», εδώ αφαιρώντας block comments τύπου 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, το οποίο είναι ακριβώς ο λόγος για τον οποίο δουλεύει ο stripper σχολίων Pascal παραπάνω.

  • Το redo εκτός grep / map δεν υποστηρίζεται. Το upstream το τεκμηριώνει ως απροσδιόριστο· μη χρησιμοποιείτε redo για να βγείτε ή να επανεκκινήσετε ένα grep BLOCK LIST ή map BLOCK LIST.

  • Το redo εκτός eval, sub, ή do δεν είναι return. Πραγματοποιεί έλεγχο ροής· δεν υπάρχει τιμή προς επιστροφή. Ένα redo που θα έπρεπε να διασχίσει τέτοιο όριο για να φτάσει σε βρόχο είναι σφάλμα χρόνου εκτέλεσης («Can’t redo outside a loop block»).

  • Ιδιορρυθμία προτεραιότητας. Σε αντίθεση με τους περισσότερους κατονομασμένους τελεστές, το redo έχει την προτεραιότητα της ανάθεσης και εξαιρείται από τον κανόνα «μοιάζει-με-συνάρτηση». Το redo ("foo")."bar" περνά ολόκληρη τη συνένωση ως όρισμα στο redo - οι παρενθέσεις δεν απομονώνουν το "foo".

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

Δείτε επίσης#

  • last - έξοδος από τον βρόχο τελείως· το μπλοκ continue δεν εκτελείται, ίδια συμπεριφορά με το redo

  • next - προχώρα στην επόμενη επανάληψη· το μπλοκ continue εκτελείται και η συνθήκη του βρόχου ελέγχεται ξανά

  • return - επιστροφή από την περικλείουσα υπορουτίνα, όχι μόνο από τον βρόχο· σε αντίθεση με το redo, παράγει τιμή

  • continue - το μπλοκ που το redo παρακάμπτει αλλά το next εκτελεί· ο πυρήνας της τριμερούς διάκρισης