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’tredooutside a loop block»).Ιδιορρυθμία προτεραιότητας. Σε αντίθεση με τους περισσότερους κατονομασμένους τελεστές, το
redoέχει την προτεραιότητα της ανάθεσης και εξαιρείται από τον κανόνα «μοιάζει-με-συνάρτηση». Τοredo ("foo")."bar"περνά ολόκληρη τη συνένωση ως όρισμα στοredo- οι παρενθέσεις δεν απομονώνουν το"foo".
Διαφορές από το upstream#
Πλήρως συμβατό με το upstream Perl 5.42.
Δείτε επίσης#
last- έξοδος από τον βρόχο τελείως· το μπλοκcontinueδεν εκτελείται, ίδια συμπεριφορά με τοredonext- προχώρα στην επόμενη επανάληψη· το μπλοκcontinueεκτελείται και η συνθήκη του βρόχου ελέγχεται ξανάreturn- επιστροφή από την περικλείουσα υπορουτίνα, όχι μόνο από τον βρόχο· σε αντίθεση με τοredo, παράγει τιμήcontinue- το μπλοκ που τοredoπαρακάμπτει αλλά τοnextεκτελεί· ο πυρήνας της τριμερούς διάκρισης