Ροή ελέγχου

goto#

Μεταφορά εκτέλεσης αλλού στο πρόγραμμα χωρίς επιστροφή.

Η goto έχει τρεις ασυσχέτιστες μορφές που μοιράζονται μόνο τη δεσμευμένη λέξη. Η χρήσιμη στη σύγχρονη Perl είναι η goto &NAME - η μορφή κλήσης ουράς, η οποία αντικαθιστά το τρέχον πλαίσιο υπορουτίνας με κλήση σε άλλη υπορουτίνα διατηρώντας το @_. Οι μορφές goto LABEL και goto EXPR υπάρχουν, είναι περιορισμένες, και σχεδόν ποτέ δεν είναι το σωστό εργαλείο.

Σύνοψη#

goto LABEL
goto EXPR
goto &NAME

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

Η goto δεν επιστρέφει. Ο έλεγχος εγκαταλείπει τη δήλωση goto και δεν επιστρέφει. Η τιμή επιστροφής της κατασκευής στην οποία τελικά προσγειώνεται η goto είναι η τιμή της περικλείουσας έκφρασης.

Για την goto &NAME, η τιμή επιστροφής είναι ό,τι επιστρέφει η υπορουτίνα-στόχος, που διαδίδεται στον αρχικό καλούντα σαν να είχε κληθεί ο στόχος άμεσα.

Η μορφή κλήσης ουράς: goto &NAME#

Αυτή είναι η μορφή που έχει σημασία. Εξέρχεται από την τρέχουσα υπορουτίνα - αναιρώντας τυχόν συνδέσεις local - και καλεί άμεσα την NAME με το τρέχον @_. Το τρέχον πλαίσιο αντικαθίσταται, δεν στοιβάζεται από πάνω.

Συνέπειες:

  • Η caller μέσα στην υπορουτίνα-στόχο βλέπει τον αρχικό καλούντα, όχι την υπορουτίνα που εξέδωσε την goto. Το ενδιάμεσο πλαίσιο έχει χαθεί.

  • Το @_ περνά ως έχει. Τροποποιήσεις στο @_ πριν την goto είναι ορατές στον στόχο.

  • Οι αλλαγές local που έγιναν στην αναχωρούσα υπορουτίνα αναιρούνται πριν τρέξει ο στόχος, ακριβώς όπως θα γίνονταν σε κανονική επιστροφή.

  • Ο στόχος δεν χρειάζεται να γνωρίζει ότι προσεγγίστηκε μέσω goto &· από τη δική του πλευρά κλήθηκε κανονικά.

Η κανονική χρήση είναι η αποστολή AUTOLOAD: αναλύστε την πραγματική υπορουτίνα, και έπειτα κάντε goto σε αυτή ώστε τα ίχνη στοίβας, η caller και η wantarray να συμπεριφέρονται σαν να είχε κληθεί η πραγματική υπορουτίνα εξαρχής.

Το NAME δεν χρειάζεται να είναι κυριολεκτικό όνομα. Μπορεί να είναι οποιαδήποτε έκφραση που παράγει αναφορά κώδικα - ένα βαθμωτό που κρατά coderef, ένα μπλοκ που επιστρέφει ένα, ή μια αποαναφερόμενη θέση.

goto &$coderef;
goto &{ $dispatch{$op} };

Για αναδρομικές κλήσεις ουράς, η goto __SUB__ μεταβαίνει στην εκτελούμενη τη στιγμή υπορουτίνα χωρίς να επαναλύει το όνομά της - ασφαλής υπό μετονομασία και ανώνυμες υπορουτίνες.

Η μορφή goto LABEL#

Βρίσκει τη δήλωση με ετικέτα LABEL εντός της τρέχουσας δυναμικής εμβέλειας και συνεχίζει την εκτέλεση εκεί. Στην πράξη αυτό σημαίνει ότι μπορεί να πηδήξει εκτός μπλοκ και εκτός υπορουτινών, αλλά δεν μπορεί να πηδήξει μέσα σε μια κατασκευή που απαιτεί αρχικοποίηση - σώμα υπορουτίνας, βρόχο foreach, μπλοκ given, την παράμετρο δυαδικού ή λίστας τελεστή, ή κώδικα που αφαίρεσε ο βελτιστοποιητής. Το πήδημα μέσα σε τέτοια κατασκευή είναι μοιραίο σφάλμα από την Perl 5.44.

Τα περισσότερα από όσα θα μπορούσε να κάνει η goto LABEL εκφράζονται καλύτερα με έλεγχο βρόχου με ετικέτα (last LABEL, next LABEL, redo LABEL) ή με εξαιρέσεις (die / eval). Καταφεύγετε πρώτα σε αυτά.

Η μορφή goto EXPR#

Αποτιμά την EXPR. Αν παράγει αναφορά κώδικα, συμπεριφέρεται όπως η goto &NAME. Αν παράγει συμβολοσειρά, αυτή η συμβολοσειρά χρησιμοποιείται ως όνομα ετικέτας που αναλύεται κατά τον χρόνο εκτέλεσης - υπολογιζόμενο goto. Οι ίδιοι περιορισμοί όπως στην goto LABEL ισχύουν για την περίπτωση της ετικέτας.

Δύο ιδιαιτερότητες του αναλυτή που πρέπει να θυμάστε:

  • Η goto EXPR εξαιρείται από τον κανόνα «μοιάζει με συνάρτηση». Παρενθέσεις μετά την goto δεν οριοθετούν απαραίτητα το όρισμά της: η goto("NE")."XT" είναι goto NEXT, όχι goto "NE" ακολουθούμενη από μια άχρηστη συνένωση.

  • Η goto έχει την ίδια προτεραιότητα με την εκχώρηση, σε αντίθεση με τους περισσότερους ονομασμένους τελεστές.

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

Αποστολή κλήσης ουράς από AUTOLOAD - η υπορουτίνα-στόχος βλέπει τον αρχικό καλούντα, όχι την AUTOLOAD:

sub AUTOLOAD {
    our $AUTOLOAD;
    my ($method) = $AUTOLOAD =~ /::(\w+)\z/;
    my $code = $self->can_really($method)
        or die "no such method: $method";
    goto &$code;              # @_ forwarded, caller() unchanged
}

Αναδρομή ουράς χωρίς αύξηση της στοίβας κλήσεων. Η __SUB__ αναλύεται στην εκτελούμενη τη στιγμή υπορουτίνα:

sub fact {
    my ($n, $acc) = @_;
    $acc //= 1;
    return $acc if $n <= 1;
    @_ = ($n - 1, $acc * $n);
    goto __SUB__;
}

Υπολογιζόμενο goto μέσω goto EXPR. Η ετικέτα επιλέγεται κατά τον χρόνο εκτέλεσης από μια λίστα:

goto ("FOO", "BAR", "GLARCH")[$i];
FOO:    handle_foo();    return;
BAR:    handle_bar();    return;
GLARCH: handle_glarch(); return;

Ανάθεση σε coderef που κρατείται σε πίνακα αποστολής. Ο καλών δεν βλέπει ποτέ το ενδιάμεσο πλαίσιο:

my %ops = (
    add => sub { $_[0] + $_[1] },
    mul => sub { $_[0] * $_[1] },
);

sub run_op {
    my $op = shift;
    goto &{ $ops{$op} // die "unknown op: $op" };
}

Τροποποίηση του @_ πριν από κλήση ουράς - οι αλλαγές είναι ορατές στον στόχο:

sub wrap {
    unshift @_, "prefix";
    goto &real_handler;       # real_handler sees ("prefix", @orig_args)
}

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

  • Η goto LABEL δεν μπορεί να εισέλθει σε υπορουτίνα, foreach ή given. Από την Perl 5.44 αυτό είναι μοιραίο σφάλμα, όχι προειδοποίηση. Επίσης δεν μπορεί να πηδήξει σε κώδικα που απαλείφθηκε από βελτιστοποίηση ή στους περισσότερους τελεστέους τελεστών - η μοναδική εξαίρεση είναι ο πρώτος τελεστέος ενός δυαδικού τελεστή (για το =, η δεξιά πλευρά).

  • Η goto LABEL δεν μπορεί να βγει από μπλοκ σύγκρισης sort. Ο συγκριτής τρέχει σε περιβάλλον που δεν επιτρέπει το πήδημα.

  • Η goto &NAME απορρίπτει συνδέσεις local από την αναχωρούσα υπορουτίνα πριν τρέξει ο στόχος. Ό,τι έχει εντοπίσει η υπορουτίνα αποκαθίσταται σαν να είχε επιστρέψει κανονικά η υπορουτίνα.

  • Η caller είναι αόρατη πέρα από goto &. Το αναχωρόν πλαίσιο έχει χαθεί. Αυτός είναι όλος ο σκοπός της μορφής για την AUTOLOAD - μη βασίζεστε στο ότι θα δείτε την ενδιάμεση υπορουτίνα σε ίχνος στοίβας.

  • Η ψευδωνυμοποίηση του @_ διατηρείται. Το @_ της υπορουτίνας-στόχου ψευδωνυμεί τα ίδια βαθμωτά που ψευδωνύμιζε το @_ της αναχωρούσας υπορουτίνας. Η εκχώρηση στο $_[0] στον στόχο εξακολουθεί να τροποποιεί το όρισμα του καλούντος.

  • Η goto &$coderef απαιτεί πραγματικό coderef. Μια συμβολοσειρά που μοιάζει με όνομα υπορουτίνας δεν λειτουργεί στη μορφή & - χρησιμοποιήστε goto EXPR (χωρίς το &) για αυτό, ή αναλύστε πρώτα το όνομα σε coderef με \&{$name}.

  • Η goto έχει προτεραιότητα εκχώρησης. Η goto "A" . "B" είναι goto "AB", όχι (goto "A") . "B".

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

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

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

  • last - έξοδος από τον πιο εσωτερικό ή έναν ονομασμένο περικλείοντα βρόχο· το σωστό εργαλείο για τις περισσότερες περιπτώσεις «πήδηξε έξω από εδώ»

  • next - μετάβαση στην επόμενη επανάληψη ενός βρόχου, με προαιρετική ετικέτα

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

  • return - έξοδος από την τρέχουσα υπορουτίνα με τιμή· συγκρίνετε με την goto &NAME που αφήνει την υπορουτίνα αντικαθιστώντας την

  • caller - επιθεώρηση της στοίβας κλήσεων· σημειώστε ότι η goto & κάνει το αναχωρόν πλαίσιο αόρατο σε αυτή