Ροή ελέγχου

next#

Ξεκινά αμέσως την επόμενη επανάληψη του περικλείοντος βρόχου.

Η next είναι το ισοδύναμο της εντολής continue της C στην Perl: εγκαταλείπει τα υπόλοιπα του σώματος της τρέχουσας επανάληψης και μεταπηδά στο βήμα επανάληψης του βρόχου. Αν ο βρόχος έχει μπλοκ continue, εκτελείται πρώτα αυτό το μπλοκ, μετά ελέγχεται ξανά η συνθήκη ελέγχου (ή λαμβάνεται το επόμενο στοιχείο για foreach), και η εκτέλεση συνεχίζεται από την κορυφή του σώματος. Με ετικέτα, η next LABEL στοχεύει σε συγκεκριμένο περικλείοντα βρόχο αντί για τον εσώτατο.

Σύνοψη#

next
next LABEL
next EXPR

Τι επιστρέφεται#

Τίποτα. Η next είναι τελεστής ελέγχου ροής, όχι έκφραση - ξετυλίγεται από την τρέχουσα επανάληψη και επομένως ποτέ δεν παράγει τιμή προς τον καλούντα της. Η προσπάθεια χρήσης της σε θέση που παράγει τιμή (για παράδειγμα ως δεξί μέλος ανάθεσης μέσα σε eval {}, sub {} ή do {}) είναι κακή χρήση: η περιβάλλουσα κατασκευή δεν ολοκληρώνεται ποτέ φυσιολογικά, οπότε δεν υπάρχει τιμή επιστροφής για παρατήρηση.

Στόχευση βρόχου#

Χωρίς ετικέτα, η next αναφέρεται στον εσώτατο περικλείοντα βρόχο (while, until, for, foreach, ή σκέτο μπλοκ, το οποίο μετράει ως βρόχος μίας εκτέλεσης). Με ετικέτα αναφέρεται στον κατονομασμένο βρόχο, ανεξαρτήτως του πόσο βαθιά εμφωλευμένη βρίσκεται η next μέσα σε άλλους βρόχους:

OUTER: for my $row (@grid) {
    for my $cell (@$row) {
        next OUTER if $cell eq 'skip-row';
        process($cell);
    }
}

Η next EXPR, διαθέσιμη από την Perl 5.18, αποτιμά την EXPR κατά τον χρόνο εκτέλεσης και χρησιμοποιεί την τιμή της ως συμβολοσειρά για το όνομα ετικέτας. Κατά τα άλλα συμπεριφέρεται ακριβώς όπως η next LABEL:

my $target = pick_label();
next $target;                   # same as: next FOO if $target eq "FOO"

Χρησιμοποιήστε τη μορφή χρόνου εκτέλεσης με φειδώ - η στατική next LABEL είναι ευκολότερο να διαβαστεί και να επαληθευτεί με το μάτι.

Αλληλεπίδραση με μπλοκ continue#

Το μπλοκ continue ενός βρόχου εκτελείται στο τέλος κάθε επανάληψης, είτε αυτή τελείωσε φυσιολογικά είτε μέσω next. Αυτό είναι όλο το νόημα της ύπαρξης μπλοκ continue: ομαδοποιεί τη λογιστική που πρέπει να συμβαίνει σε κάθε πέρασμα.

my $i = 0;
LINE: while (my $line = <$fh>) {
    next LINE if $line =~ /^\s*#/;  # skip comments
    process($line);
}
continue {
    $i++;                            # runs even for skipped lines
}

Η last παρακάμπτει το μπλοκ continue· η next όχι. Αυτή είναι η απλούστερη διακριτική ιδιότητα των δύο, και ο λόγος που το continue υπάρχει ως ξεχωριστή κατασκευή αντί να ενσωματώνεται στο σώμα του βρόχου.

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

Βασικός βρόχος while - απόρριψη γραμμών σχολίων, επεξεργασία των υπολοίπων:

while (my $line = <$fh>) {
    next if $line =~ /^\s*#/;
    next if $line =~ /^\s*$/;       # blank
    handle($line);
}

foreach με συσσωρευτή - παράκαμψη εγγραφών που αποτυγχάνουν σε κατηγόρημα:

my $total = 0;
for my $n (@values) {
    next unless defined $n;
    next if $n < 0;
    $total += $n;
}

Εμφωλευμένοι βρόχοι με ετικέτα - προχωρούμε στην επόμενη εξωτερική γραμμή μόλις εντοπίσουμε φρουρά στην εσωτερική σάρωση:

ROW: for my $row (@grid) {
    for my $cell (@$row) {
        next ROW if $cell eq 'END';
        count($cell);
    }
}

Μπλοκ continue που εκτελείται πάντα, ακόμα και σε next:

my @kept;
ITEM: for my $x (@input) {
    next ITEM if $x->{hidden};
    push @kept, $x;
}
continue {
    log_seen($_);                    # every $x is logged, kept or not
}

Διαφυγή από σκέτο μπλοκ - ένα αυτόνομο { ... } είναι βρόχος μίας εκτέλεσης, οπότε η next υπερπηδά τις υπόλοιπες εντολές και βγαίνει από αυτό:

{
    last_chance() or next;           # bare block: next == fall out
    commit();
}

Οριακές περιπτώσεις#

  • Παράνομη μέσα σε grep / map / μπλοκ τιμής do { }. Αυτές οι κατασκευές δεν είναι βρόχοι - το μπλοκ τους αποτιμάται για την τιμή του, δεν επαναλαμβάνεται με την έννοια του ελέγχου βρόχου. Η χρήση της next για να βγείτε από αυτά είναι κακή χρήση και επισημαίνεται ρητά στο upstream perlfunc. Χρησιμοποιήστε αντ’ αυτού υπό συνθήκη έκφραση:

    my @kept = grep { wanted($_) } @xs;           # not: grep { next unless ... }
    my @mapped = map { transform($_) // () } @ys; # empty list == skip
    
  • Δεν είναι return. Η next δεν μπορεί να παραδώσει τιμή έξω από μπλοκ που επιστρέφει τιμή όπως eval { }, sub { } ή do { }. Αν βρεθείτε να καταφεύγετε στην next για να φύγετε από τέτοιο μπλοκ, αυτό που θέλετε είναι η return, ή μια υπό συνθήκη έκφραση που παράγει την τιμή που χρειάζεστε.

  • Ένα σκέτο μπλοκ είναι βρόχος μίας εκτέλεσης. Το { ... } χωρίς πρόθεμα while / for είναι σημασιολογικά βρόχος που εκτελείται ακριβώς μία φορά, οπότε η next βγαίνει από αυτό. Αυτό είναι ενίοτε χρήσιμο για μοτίβα πρόωρης εξόδου, αλλά η last είναι η συχνότερη επιλογή επειδή καθιστά την πρόθεση σαφέστερη.

  • Άγνωστη ετικέτα είναι μοιραίο σφάλμα. Η next NOSUCH πεθαίνει κατά τον χρόνο εκτέλεσης με Can't "next" outside a loop block όταν κανένας περικλείων βρόχος δεν φέρει αυτήν την ετικέτα.

  • Το continue εκτελείται σε next, όχι σε last. Αυτή είναι η συνηθισμένη αιτία έκπληξης κατά την αναδιάρθρωση: η μετακίνηση λογιστικής έξω από το σώμα του βρόχου στο continue αλλάζει συμπεριφορά μόνο για την last, όχι για την next.

  • Ιδιορρυθμίες προτεραιότητας και ανάλυσης. Σε αντίθεση με τους περισσότερους κατονομασμένους τελεστές, η next έχει την ίδια προτεραιότητα με την ανάθεση, και εξαιρείται από τον κανόνα «μοιάζει-με-συνάρτηση». Αυτό σημαίνει ότι η next ("foo") . "bar" περνά το "foobar" στην next, όχι μόνο το "foo". Χρησιμοποιήστε αμυντικά παρενθέσεις όταν το όνομα ετικέτας προέρχεται από έκφραση:

    next(($cond ? "A" : "B"));
    
  • Threads, σήματα, καταστροφείς. Η next ξετυλίγει τη λεξιλογική εμβέλεια της τρέχουσας επανάληψης: οι μεταβλητές my βγαίνουν από την εμβέλεια, οι τιμές local επανέρχονται, και ο DESTROY πυροδοτείται σε αντικείμενα εξόδου εμβέλειας, ακριβώς όπως αν η επανάληψη είχε τερματιστεί φυσιολογικά. Οι εκκρεμείς χειριστές $SIG{...} δεν επηρεάζονται.

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

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

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

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

  • redo - επανεκκίνηση της τρέχουσας επανάληψης χωρίς εκ νέου έλεγχο της συνθήκης και χωρίς εκτέλεση του continue

  • return - το σωστό εργαλείο για έξοδο από sub { }, eval { } ή do { } με τιμή· η next δεν μπορεί να το κάνει αυτό

  • grep - φιλτράρει μια λίστα· χρησιμοποιήστε υπό συνθήκη έκφραση, όχι next, για να απορρίψετε στοιχεία

  • map - μετασχηματίζει μια λίστα· επιστρέψτε την κενή λίστα () από το μπλοκ για να απορρίψετε ένα στοιχείο