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'για μεθόδους