Ροή ελέγχου

SUB#

Επιστρέφει μια αναφορά στην υπορουτίνα που εκτελείται τη δεδομένη στιγμή.

Το __SUB__ είναι το σύμβολο αυτο-αναφοράς: μέσα σε μια sub, αποτιμάται σε μια αναφορά κώδικα που δείχνει σε αυτήν την ίδια sub, χωρίς η sub να χρειάζεται να γνωρίζει το όνομά της. Εκτός κάθε sub, είναι undef. Ο συνηθισμένος λόγος για την προσφυγή σε αυτό είναι η ανώνυμη αναδρομή - μια sub αποθηκευμένη σε λεξιλογική μεταβλητή ή που περνά ως callback χρειάζεται έναν τρόπο να καλέσει τον εαυτό της που δεν εξαρτάται από όνομα κατονομασμένο πλήρως με πακέτο, και το __SUB__ είναι αυτός ο τρόπος.

Σύνοψη#

__SUB__
__SUB__->(@args)
goto __SUB__

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

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

Μια αναφορά κώδικα (το ίδιο πράγμα που θα σας έδινε το \&name για μια κατονομασμένη sub), ή undef όταν αποτιμάται εκτός οποιασδήποτε υπορουτίνας. Η αναφορά είναι προς την sub σε χρόνο εκτέλεσης, συμπεριλαμβανομένης κάθε closure πάνω σε λεξιλογικές μεταβλητές που αποτυπώθηκαν όταν δημιουργήθηκε η sub, οπότε η αναδρομική κλήση της βλέπει το ίδιο αποτυπωμένο περιβάλλον.

my $fact = sub {
    my $n = shift;
    $n < 2 ? 1 : $n * __SUB__->($n - 1);
};

print $fact->(5);               # 120

Η μορφή κλήσης είναι μια συνηθισμένη επίκληση coderef: __SUB__->(@args) ή &{__SUB__}(@args). Κανένα όρισμα δεν περνά σιωπηρά· η αναδρομή πρέπει να τα προωθεί ρητά.

Γιατί να μη χρησιμοποιήσουμε απλώς το όνομα της sub#

Οι κατονομασμένες subs μπορούν ήδη να εκτελούν αναδρομή με το όνομά τους - το factorial(...) μέσα στη sub factorial δουλεύει. Το __SUB__ δικαιολογεί τη θέση του σε τρεις περιπτώσεις όπου ένα όνομα δεν είναι διαθέσιμο, αξιόπιστο ή ακριβό:

  • Ανώνυμες subs. Μια ανώνυμη sub που εκχωρείται σε λεξιλογική μεταβλητή δεν έχει σταθερό όνομα κατονομασμένο με πακέτο. Ένας συνηθισμένος τρόπος παράκαμψης πριν την 5.16 ήταν η αποτύπωση μιας αναφοράς προς \&name ή η χρήση κόλπων Y-combinator· το __SUB__ αντικαθιστά και τα δύο.

  • Subs που περνούν ως callbacks. Μια sub που παραδίδεται σε ένα άλλο API (συγκριτής ταξινόμησης, χειριστής συμβάντων, βήμα επαναλήπτη) ενδέχεται να κληθεί πίσω προτού αποκτήσει όνομα ορατό στο σημείο της αναδρομής.

  • Αναδρομή με κλήση ουράς μέσω goto. Το goto __SUB__ επαναχρησιμοποιεί το τρέχον πλαίσιο κλήσης και μεταπηδά στην κορυφή της ίδιας sub με όποια τιμή του @_ έχετε ρυθμίσει, κάτι που έχει σημασία για βαθιά αναδρομή που διαφορετικά θα ανατίναζε τη στοίβα.

Καθολική κατάσταση που επηρεάζει#

Το __SUB__ διαβάζει το πλαίσιο κλήσης που εκτελείται τη δεδομένη στιγμή - το ίδιο πλαίσιο που επιθεωρεί ο caller. Δεν διαβάζει ούτε τροποποιεί ειδικές μεταβλητές.

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

Ανώνυμο παραγοντικό, η σχολική περίπτωση:

my $fact = sub {
    my $n = shift;
    $n < 2 ? 1 : $n * __SUB__->($n - 1);
};
print $fact->(6);               # 720

Άθροισμα με αναδρομή ουράς μέσω goto __SUB__ - χωρίς αύξηση στοίβας ανά επανάληψη, επειδή το goto επαναχρησιμοποιεί το τρέχον πλαίσιο:

my $sum = sub {
    my ($acc, @rest) = @_;
    return $acc unless @rest;
    @_ = ($acc + shift @rest, @rest);
    goto __SUB__;
};
print $sum->(0, 1..1_000_000);  # 500000500000

Διάσχιση εμφωλευμένης δομής δεδομένων χωρίς να κατονομαστεί ο διασχιστής:

my $count_leaves = sub {
    my $node = shift;
    return 1 unless ref $node eq 'ARRAY';
    my $n = 0;
    $n += __SUB__->($_) for @$node;
    return $n;
};
print $count_leaves->([1, [2, 3], [[4], 5]]);   # 5

Εκτός οποιασδήποτε sub, το __SUB__ είναι undef:

print defined(__SUB__) ? "yes" : "no";          # no

Αποτυπωμένο, όχι αναλυόμενο κατά την κλήση. Το __SUB__ αποτιμάται κάθε φορά που εκτελείται, στο πλαίσιο εντός του οποίου εκτελείται - έτσι σε μια εμφωλευμένη sub αναφέρεται στην εσωτερική sub:

sub outer {
    my $inner = sub { __SUB__ };    # inner's own coderef
    return $inner->();
}
my $ref = outer();
print $ref == $ref;                 # 1 - stable coderef

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

  • Απαιτεί το χαρακτηριστικό current_sub. Το __SUB__ βρίσκεται πίσω από use feature 'current_sub', ενεργοποιημένο εξ ορισμού υπό use v5.16 ή οποιοδήποτε μεταγενέστερο πακέτο εκδόσεων. Κώδικας που στοχεύει παλαιότερα επίπεδα σύνταξης πρέπει να το ενεργοποιήσει ρητά, ή να προθέσει στο σύμβολο το CORE:: ως CORE::__SUB__.

  • Χωρίς μορφή με παρενθέσεις. Το __SUB__() είναι συντακτικό σφάλμα - το σύμβολο δεν είναι συνάρτηση, είναι δεσμευμένη λέξη που αποτιμάται σε coderef. Καλέστε το αποτέλεσμα με __SUB__->() ή &{__SUB__}().

  • Εκτός οποιασδήποτε sub, συμπεριλαμβανομένης της εμβέλειας αρχείου και του εσωτερικού των μπλοκ BEGIN, UNITCHECK, CHECK, INIT και END, το __SUB__ επιστρέφει undef. Η κλήση του (__SUB__->()) σε αυτές τις θέσεις πεθαίνει με Can't use an undefined value as a subroutine reference.

  • Μέσα σε μπλοκ eval, το __SUB__ αναφέρεται στην περικλείουσα sub, όχι στην eval - ένα eval { ... } δεν δημιουργεί νέο πλαίσιο sub για αυτόν τον σκοπό. Μέσα σε eval EXPR ισχύει ο ίδιος κανόνας: το σύμβολο αναλύεται στην εξωτερική sub.

  • Μέσα σε μπλοκ κώδικα regex (/(?{...})/ ή /(??{...})/), η συμπεριφορά του __SUB__ υπόκειται σε αλλαγές - το upstream το τεκμηριώνει ως ασταθές. Μη βασίζεστε σε αυτό που επιστρέφει εκεί.

  • Ταυτότητα μεταξύ κλήσεων. Δύο αποτιμήσεις του __SUB__ μέσα στην ίδια sub επιστρέφουν το ίδιο coderef (η == συγκρίνει ως αληθής). Μεταξύ διαφορετικών closures που φτιάχνονται από το ίδιο sub { ... }, κάθε closure βλέπει το δικό του coderef - ένα ανά αποτυπωμένο περιβάλλον.

  • goto __SUB__ έναντι __SUB__->(). Η μορφή goto επαναχρησιμοποιεί το τρέχον πλαίσιο κλήσης και αντικαθιστά το @_ με ό,τι αναθέσατε πριν το goto· δεν μεγαλώνει τη στοίβα κλήσεων. Η μορφή κλήσης coderef δημιουργεί νέο πλαίσιο κάθε φορά, οπότε χτυπά το όριο βάθους αναδρομής σε βαθιά αναδρομικές εισόδους.

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

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

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

  • sub - ορίζει την υπορουτίνα της οποίας την αναφορά επιστρέφει το __SUB__· το πρώτο μέρος που πρέπει να κοιτάξετε όταν η ανώνυμη αναδρομή είναι το εργαλείο για τη δουλειά

  • goto - συνεργάζεται με το __SUB__ για αναδρομή με κλήση ουράς μέσω goto __SUB__, το ιδίωμα που αποφεύγει την αύξηση της στοίβας

  • caller - επιθεώρηση του ίδιου πλαισίου κλήσης από έξω (πακέτο, αρχείο, γραμμή) αντί για λήψη αναφοράς σε αυτό

  • ref - κατηγοριοποίηση αυτού που επέστρεψε το __SUB__· πάντα "CODE" μέσα σε sub, κενή συμβολοσειρά εκτός

  • return - ο συνηθισμένος τρόπος εξόδου από τη sub της οποίας η αναφορά είναι το __SUB__· σχετικός όταν επιλέγει κανείς μεταξύ αναδρομής και κλήσης ουράς μέσω goto __SUB__