Ροή ελέγχου

sub#

Δηλώνει ή ορίζει μια υπορουτίνα.

Η sub είναι η δεσμευμένη λέξη που εισάγει μια υπορουτίνα. Με NAME και BLOCK ορίζει μια κατονομασμένη υπορουτίνα στο τρέχον πακέτο. Χωρίς BLOCK είναι δήλωση προς τα εμπρός. Χωρίς NAME είναι ανώνυμη έκφραση sub που αποτιμάται σε αναφορά κώδικα - το sub { ... } είναι η μόνη μορφή της sub που είναι πράγματι έκφραση· οι κατονομασμένες μορφές είναι δηλώσεις και δεν επιστρέφουν τίποτα χρήσιμο.

Σύνοψη#

sub NAME BLOCK                         # named definition
sub NAME (PROTO) BLOCK                 # with prototype
sub NAME : ATTRS BLOCK                 # with attributes
sub NAME (PROTO) : ATTRS BLOCK         # prototype + attributes

sub NAME;                              # forward declaration
sub NAME (PROTO);                      # forward with prototype

my $ref = sub BLOCK;                   # anonymous, returns code ref
my $ref = sub (PROTO) : ATTRS BLOCK;   # anonymous with proto + attrs

use feature 'signatures';
sub NAME ($x, $y = 0, @rest) BLOCK     # named with signature
my $ref = sub ($x, $y) BLOCK;          # anonymous with signature

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

Οι κατονομασμένες μορφές (sub NAME BLOCK, sub NAME (PROTO) BLOCK, κ.λπ.) είναι δηλώσεις. Εγκαθιστούν την υπορουτίνα στον πίνακα συμβόλων του τρέχοντος πακέτου κατά τη μεταγλώττιση και δεν συνεισφέρουν τίποτα στην περιβάλλουσα έκφραση. Η γραφή my $x = sub foo { 1 }; είναι συνηθισμένο λάθος - αυτό είναι δύο δηλώσεις κολλημένες μαζί, και η $x αφήνεται απροσδιόριστη.

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

my @refs = map { sub { $_ } } 1..3;     # three independent closures

Οι δηλώσεις προς τα εμπρός (sub NAME; χωρίς μπλοκ) δεν κάνουν τίποτα σε χρόνο εκτέλεσης και δεν επιστρέφουν τίποτα. Σκοπός τους είναι να αναγγείλουν το όνομα στον αναλυτή ώστε επόμενες κλήσεις του να μπορούν να αναλυθούν χωρίς παρενθέσεις.

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

  • Το τρέχον πακέτο (ορίζεται από την package) καθορίζει το πλήρως κατονομασμένο όνομα μιας κατονομασμένης υπορουτίνας. Το sub foo { } μέσα σε package Acme; εγκαθιστά την Acme::foo, όχι την main::foo.

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

  • Τα στοιχεία $_[N] είναι ψευδώνυμα προς τα βαθμωτά του καλούντος. Η τροποποίηση της $_[0] τροποποιεί τη μεταβλητή του καλούντος· η διαβίβαση κυριολεκτικής τιμής και η μετέπειτα τροποποίηση της $_[0] είναι μοιραίο σφάλμα.

  • Η wantarray μέσα στο σώμα αντικατοπτρίζει το περιβάλλον στο οποίο κλήθηκε η υπορουτίνα.

Κατονομασμένος ορισμός#

sub greet {
    my ($name) = @_;
    return "Hello, $name";
}

print greet("world"), "\n";            # Hello, world

Το σώμα μεταγλωττίζεται κατά τη μεταγλώττιση. Το όνομα είναι ορατό από εκείνο το σημείο του αρχείου και μετά· οι μπροστινές αναφορές εντός του ίδιου πακέτου λειτουργούν διότι η Perl αναβάλλει τις κλήσεις τύπου μεθόδου και τις κλήσεις bareword.

Ο επανορισμός κατονομασμένης υπορουτίνας σε χρόνο εκτέλεσης (eval "sub foo { ... }" ή ένα δεύτερο sub foo { } στο ίδιο πακέτο) αντικαθιστά τον προηγούμενο ορισμό και εκπέμπει προειδοποίηση Subroutine foo redefined υπό use warnings. Καταστείλετέ την με no warnings 'redefine' για την εμβέλεια του επανορισμού.

Ανώνυμη sub και closures#

sub make_counter {
    my $n = 0;
    return sub { ++$n };
}

my $c = make_counter();
print $c->(), $c->(), $c->(), "\n";    # 123

Η εσωτερική sub αποτυπώνει την $n από την περικλείουσα εμβέλειά της. Κάθε κλήση της make_counter δημιουργεί μια καινούργια $n και ένα καινούργιο closure πάνω σε αυτή.

my @doublers = map { my $k = $_; sub { $k * 2 } } 1..3;
print $doublers[2]->(), "\n";          # 6

Παρατηρήστε το ενδιάμεσο my $k = $_ - τα closures αποτυπώνουν μεταβλητές, όχι τιμές, και η $_ είναι κοινή στις επαναλήψεις της map. Χωρίς το αντίγραφο, κάθε closure θα έβλεπε την τελευταία τιμή της $_.

Υπογραφές#

Με use feature 'signatures' (ενεργό προεπιλεγμένα υπό use v5.36 και μεταγενέστερα), η λίστα παραμέτρων εμφανίζεται ανάμεσα στο όνομα και το μπλοκ, και τα ορίσματα δένονται αυτόματα σε λεκτικές μεταβλητές:

use feature 'signatures';

sub add ($x, $y) { $x + $y }

sub greet ($name, $greeting = "Hello") {
    return "$greeting, $name";
}

sub log_all ($level, @msgs) {
    print "[$level] $_\n" for @msgs;
}

Μορφές υπογραφών:

  • $x - απαιτούμενη παράμετρος κατά θέση.

  • $x = EXPR - προαιρετική παράμετρος κατά θέση με προεπιλογή· η EXPR αποτιμάται σε κάθε κλήση όταν το όρισμα λείπει.

  • $x = undef - προαιρετική, με προεπιλογή undef (χρήσιμη για να τεκμηριωθεί ότι μια θυρίδα είναι nullable αντί να παραλείπεται).

  • @rest / %rest - slurpy, καταναλώνει όλα τα υπόλοιπα ορίσματα.

  • $ / @ / % - ακατονόμαστο placeholder, επιβεβαιώνει την πληθικότητα χωρίς να δεσμεύει μεταβλητή.

Κλήση με λανθασμένη πληθικότητα κράζει: Too many arguments for subroutine ή Too few arguments for subroutine.

Υπό υπογραφή, η @_ εξακολουθεί να υπάρχει αλλά η πρόσβαση σε αυτή αποτελεί προειδοποίηση υπό use warnings 'experimental::args_array_with_signatures' σε παλαιότερες Perl και αποθαρρύνεται στην 5.42. Χρησιμοποιήστε αντ” αυτής τις μεταβλητές της υπογραφής.

Πρωτότυπα#

sub mypush (\@@) {
    my $aref = shift;
    push @$aref, @_;
}

my @a = (1, 2);
mypush @a, 3, 4;                       # @a is now (1, 2, 3, 4)

Τα πρωτότυπα είναι υπόδειξη προς τον αναλυτή κατά τη μεταγλώττιση, όχι έλεγχος τύπου. Αλλάζουν τον τρόπο που αναλύονται τα σημεία κλήσης: το \@ στο παραπάνω πρωτότυπο εξαναγκάζει το πρώτο όρισμα να είναι πίνακας και διαβιβάζει αναφορά σε αυτόν. Δείτε την prototype για να επιθεωρήσετε το πρωτότυπο μιας υπορουτίνας.

Τα πρωτότυπα δεν κάνουν τίποτα όταν η υπορουτίνα καλείται μέσω &name(...) ή μέσω αναφοράς κώδικα. Αν θέλετε υπογραφή, χρησιμοποιήστε use feature 'signatures'· τα πρωτότυπα και οι υπογραφές μπορούν να συνυπάρχουν με το γνώρισμα :prototype(...):

use feature 'signatures';
sub each_pair :prototype(\@) ($aref) { ... }

Γνωρίσματα#

Τα γνωρίσματα εμφανίζονται μετά από : και τροποποιούν τον τρόπο που η υπορουτίνα μεταγλωττίζεται ή καταχωρείται:

sub critical :lvalue { $state }        # usable on the left of =
sub noop     :method { }               # marked as a method (for some tools)

Συνηθισμένα βασικά γνωρίσματα: lvalue, method, prototype(...), const. Αρθρώματα όπως το Attribute::Handlers επιτρέπουν σε ένα πακέτο να ορίσει τα δικά του. Άγνωστα γνωρίσματα αποτελούν σφάλμα κατά τη μεταγλώττιση.

Δήλωση προς τα εμπρός#

sub later;                             # announce the name
plan later(3);                         # now parses without parens
sub later { ... }                      # defined later in the file

Μια δήλωση προς τα εμπρός είναι χρήσιμη μόνο για να ενημερώσει τον αναλυτή ότι το later είναι υπορουτίνα, ώστε το later ARG να αναλυθεί ως κλήση και όχι ως κατασκευή έμμεσου αντικειμένου ή έκφραση με κόμμα. Δεν απαιτείται αν καλείτε με παρενθέσεις.

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

Κατονομασμένη υπορουτίνα, ρητή αποσυσκευασία ορισμάτων:

sub area {
    my ($w, $h) = @_;
    return $w * $h;
}
print area(3, 4), "\n";                # 12

Ανώνυμη υπορουτίνα που διαβιβάζεται σε συνάρτηση ανώτερης τάξης:

my @lengths = map { sub { length shift } }->(@_),
              ("alpha", "beta", "gamma");   # not quite - see below

Πιο χρήσιμο: διαβίβαση sub απευθείας:

use List::Util qw(first);
my $first_even = first { $_ % 2 == 0 } 1, 3, 4, 7;   # 4

Υπονοούμενη επιστροφή - η τιμή της τελευταίας έκφρασης επιστρέφεται:

sub double { $_[0] * 2 }               # no return needed
print double(21), "\n";                # 42

Επιστροφή ευαίσθητη στο περιβάλλον:

sub names {
    my @n = qw(alice bob carol);
    return wantarray ? @n : scalar @n;
}

my @all = names();                     # ("alice","bob","carol")
my $n   = names();                     # 3

Τροποποίηση ορισμάτων καλούντος μέσω ψευδωνυμοποίησης της @_:

sub upcase_in {
    for (@_) { $_ = uc }
}
my $s = "hello";
upcase_in($s);
print $s, "\n";                        # HELLO

Αναδρομή με την __SUB__ - αναφορά στην τρέχουσα υπορουτίνα χωρίς να την ονομάσετε:

use feature 'current_sub';
my $fact = sub {
    my $n = shift;
    $n < 2 ? 1 : $n * __SUB__->($n - 1);
};
print $fact->(5), "\n";                # 120

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

  • Ο κατονομασμένος ορισμός sub είναι δήλωση, όχι έκφραση. Δεν συνεισφέρει τίποτα στην περικλείουσα έκφρασή του. Το my $x = sub foo {}; αναλύεται ως δύο δηλώσεις· η $x καταλήγει undef.

  • Οι μορφές sub NAME BLOCK είναι κατά τη μεταγλώττιση. Το σώμα του μπλοκ αναλύεται και μεταγλωττίζεται όταν μεταγλωττίζεται το περικλείον αρχείο ή eval. Οι παρενέργειες μέσα στο μπλοκ τρέχουν μόνο όταν η υπορουτίνα καλείται, όχι όταν ορίζεται. Αντιθέτως, η sub BLOCK (ανώνυμη) αποτιμάται σε χρόνο εκτέλεσης κάθε φορά που η έκφραση προσεγγίζεται, παράγοντας ένα νέο closure κάθε φορά.

  • Οι εμφωλευμένες κατονομασμένες υπορουτίνες δεν κλείνουν πάνω σε τίποτα χρήσιμο. Μια sub ορισμένη μέσα σε άλλη sub εξακολουθεί να εγκαθίσταται στο πακέτο κατά τη μεταγλώττιση και δεν αποτυπώνει λεκτικές μεταβλητές από την περικλείουσα κλήση. Κάθε κλήση της εξωτερικής υπορουτίνας βλέπει την ίδια εσωτερική υπορουτίνα, στρεβλωμένη γύρω από τις λεκτικές της πρώτης κλήσης. Χρησιμοποιήστε ανώνυμη υπορουτίνα για closures.

  • Η τελική return είναι προαιρετική αλλά η ρητή είναι πιο καθαρή. Χωρίς return, επιστρέφεται η τιμή της τελευταίας έκφρασης. Αν η τελευταία δήλωση είναι βρόχος ή μπλοκ, η τιμή επιστροφής είναι απροσδιόριστη - μη βασίζεστε σε αυτή.

  • Η ανάθεση στην @_ συνολικά σπάει την ψευδωνυμοποίηση. Το @_ = @_ ή @_ = (1, 2) αντικαθιστά τον πίνακα και αργότερα $_[0] = X δεν φτάνει πια στον καλούντα.

  • Η διαβίβαση κυριολεκτικής τιμής και η μετέπειτα τροποποίηση της $_[0] είναι μοιραία. Το upcase_in("x") πεθαίνει με Modification of a read-only value attempted διότι η $_[0] είναι ψευδώνυμο της κυριολεκτικής τιμής.

  • Ο επανορισμός υπορουτίνας εκπέμπει Subroutine X redefined υπό use warnings. Το τύλιγμα ενός επανορισμού απαιτεί no warnings 'redefine' για την εμβέλεια:

    no warnings 'redefine';
    *Foo::bar = sub { ... };
    
  • sub μέσα σε μπλοκ BEGIN ορίζεται κατά τη μεταγλώττιση της περιβάλλουσας μονάδας, όπως κάθε δήλωση sub· το τύλιγμα BEGIN δεν αλλάζει τον χρονισμό της ίδιας της δήλωσης.

  • Παγίδα πρωτοτύπου μετά από κενό. Το sub foo ($$) με use feature 'signatures' αναλύεται ως υπογραφή, όχι ως πρωτότυπο. Για να επισυνάψετε πρωτότυπο υπό υπογραφές, χρησιμοποιήστε sub foo :prototype($$) ($x, $y) { ... }.

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

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

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

  • return - ρητή επιστροφή από υπορουτίνα· χωρίς αυτή επιστρέφεται η τιμή της τελευταίας έκφρασης

  • wantarray - επιθεωρεί το περιβάλλον κλήσης (λίστα / βαθμωτό / κενό) από μέσα από το σώμα

  • caller - πληροφορίες για το ποιος κάλεσε αυτή την υπορουτίνα και από πού

  • prototype - διαβάστε πίσω το πρωτότυπο μιας κατονομασμένης ή ανώνυμης υπορουτίνας

  • __SUB__ - αναφορά στην τρέχουσα υπορουτίνα χωρίς να την ονομάσετε· το εργαλείο για ανώνυμη αναδρομή

  • my - ο συνηθισμένος τρόπος για αποσυσκευασία της @_ σε κατονομασμένες λεκτικές μεταβλητές όταν δεν χρησιμοποιούνται υπογραφές

  • method - δήλωση μεθόδου με σύνταξη κλάσης· το ισοδύναμο της sub υπό use feature 'class' για μεθόδους