Υπογραφές#

Μια υπογραφή είναι μια λίστα παραμέτρων σε παρενθέσεις που τοποθετείται αμέσως μετά το όνομα της sub (και μετά τα γνωρίσματα), στη θέση - ή επιπλέον - της χειροκίνητης αποσυσκευασίας @_ του σώματος. Οι υπογραφές εισάγουν λεξιλογικές μεταβλητές παραμέτρων, εκτελούν έλεγχο πληθικότητας κατά τον χρόνο κλήσης, και υποστηρίζουν προεπιλεγμένες τιμές και slurpy παραμέτρους.

Signatures stabilised in Perl 5.36; in 5.44 they are a production feature available with one of:

use feature 'signatures';        # explicit feature import
use v5.36;                       # implicit (signatures + other 5.36 features)

PetaPerl supports the full 5.44 signature syntax.

Βασική μορφή#

use feature 'signatures';

sub add ($x, $y) {               # two mandatory positional parameters
    return $x + $y;
}

add(2, 3);                       # 5
add(2);                          # error: Too few arguments
add(2, 3, 4);                    # error: Too many arguments

Κάθε $name στην υπογραφή δηλώνει μια λεξιλογική my που λαμβάνει το αντίστοιχο όρισμα. Η πληθικότητα ελέγχεται κατά την κλήση: το σώμα δεν εκτελείται ποτέ αν ο αριθμός είναι λάθος.

Αυτή είναι η πιο συγκεκριμένη βελτίωση σε σχέση με την κλασική μορφή:

# Classic - silently accepts wrong arity
sub add {
    my ($x, $y) = @_;
    return $x + $y;
}

add(2);                          # $y is undef; later arithmetic warns
add(2, 3, 4);                    # third arg silently ignored

Προεπιλεγμένες τιμές#

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

greet('Alice');                  # "Hello, Alice!"
greet('Alice', 'Hi');            # "Hi, Alice!"

Η προεπιλεγμένη τιμή είναι μια αυθαίρετη έκφραση που αποτιμάται κάθε φορά που η παράμετρος παραλείπεται (οπότε λειτουργούν τόσο state όσο και υπολογισμός ανά κλήση). Μπορεί να αναφέρεται σε προηγούμενες παραμέτρους:

sub log_event ($message, $level = 'INFO', $prefix = "[$level]") {
    print "$prefix $message\n";
}

log_event('starting');                      # [INFO] starting
log_event('failure', 'ERROR');              # [ERROR] failure
log_event('done', 'OK', '>>');              # >> done

Μια προεπιλογή = undef είναι ακριβώς ισοδύναμη με «χωρίς προεπιλογή» για την πληθικότητα (η παράμετρος εξακολουθεί να είναι προαιρετική) αλλά τεκμηριώνει την πρόθεση. Χρησιμοποιήστε φρουρούς τύπου //= μέσα στο σώμα για σημασιολογία «η undef σημαίνει χρήση της εφεδρικής τιμής μου».

Προαιρετικές έναντι υποχρεωτικών#

Οι παράμετροι είναι υποχρεωτικές εξ ορισμού. Μια παράμετρος γίνεται προαιρετική:

  • Δίνοντάς της μια προεπιλεγμένη τιμή ($x = 42).

  • Τοποθετώντας την μετά από άλλη προαιρετική παράμετρο (μεταβατικά).

  • Ακολουθούμενη από slurpy @arr ή %hash.

Δεν μπορείτε να έχετε υποχρεωτική παράμετρο μετά από προαιρετική (εκτός μέσω slurpy):

sub bad ($x = 1, $y) { ... }     # error: Mandatory parameter follows optional

Παράμετροι slurpy#

Η τελευταία παράμετρος μπορεί να είναι slurpy @list ή %hash. Καταναλώνει όλα τα εναπομείναντα ορίσματα:

sub join_all ($sep, @parts) {
    return join $sep, @parts;
}

join_all(', ', 'a', 'b', 'c');   # "a, b, c"

sub configure ($name, %opts) {
    $opts{verbose} //= 0;
    ...
}

configure('demo', verbose => 1, port => 8080);

Το slurpy hash αποτυγχάνει επίσης αν η εναπομείνασα λίστα έχει μονό μήκος:

configure('demo', 'verbose');    # error: Odd-sized list given

Αυτό είναι το πιο χρήσιμο μοτίβο που ξεκλειδώνουν οι υπογραφές: ένα API ονομαστικών παραμέτρων με μία κατά θέση και ένα slurp %opts.

Ανώνυμες παράμετροι#

Ένα $ (ή @, %) χωρίς όνομα είναι ένα placeholder κατά θέση:

sub second ($, $x) { return $x }

second('ignored', 'kept');       # "kept"

Αυτό είναι ελαφρώς χρήσιμο όταν προσαρμόζετε ένα callback σε μια διεπαφή που παρέχει περισσότερα ορίσματα από όσα σας ενδιαφέρουν.

Συνδυασμός προεπιλεγμένων τιμών και slurpy#

sub render ($template, $cache = 1, %opt) {
    ...
}

render('greeting.tt');                       # cache=1, %opt empty
render('greeting.tt', 0);                    # cache=0, %opt empty
render('greeting.tt', 1, locale => 'de');    # all three
render('greeting.tt', locale => 'de');       # ERROR: 'locale' becomes $cache
                                             # then 'de' is the lone slurp arg
                                             # (odd-length hash) - diagnose carefully

Αυτή η τελευταία περίπτωση είναι η παγίδα: μια προαιρετική παράμετρος κατά θέση ακολουθούμενη από slurpy hash είναι αμφίσημη σε κλήσεις μόνο-ονομαστικών-ορισμάτων. Είτε αφαιρέστε την προαιρετική κατά θέση και βάλτε τα πάντα στο hash, είτε περνάτε πάντα την προαιρετική κατά θέση ρητά.

Μετάβαση από αποσυσκευασία @_#

Η μηχανική μετάφραση:

# Before
sub make_user {
    my ($name, $email, %extra) = @_;
    ...
}

# After
use feature 'signatures';
sub make_user ($name, $email, %extra) {
    ...
}

Αποκτάτε δωρεάν έλεγχο πληθικότητας και ονομαστικές λεξιλογικές. Το σώμα παραμένει αμετάβλητο.

Τα σημεία όπου δεν μπορείτε να μεταφράσετε μηχανικά:

  • Το σώμα χρησιμοποιεί δυναμικά τον αριθμό ορισμάτων (if (@_ >= 3)) - οι υπογραφές καθορίζουν πληθικότητα κατά τον χρόνο ανάλυσης, οπότε αναδιαμορφώστε σε προαιρετική παράμετρο με προεπιλογή.

  • Το σώμα τροποποιεί τo $_[0] για το αποτέλεσμα ψευδωνυμοποίησης - κρατήστε την κλασική μορφή, καθώς οι υπογραφές εισάγουν αντίγραφα, σπάζοντας το ψευδώνυμο.

  • The body uses goto &other and wants to forward the original @_ - @_ is still populated under signatures in 5.44, but this is implementation-defined and may change. Keep the classic form for tail-call forwarding.

Υπογραφές και πρωτότυπα είναι ορθογώνια#

Οι υπογραφές και τα πρωτότυπα λύνουν διαφορετικά προβλήματα:

Μηχανισμός

Επηρεάζει

Πότε ελέγχεται

Χρήσιμο για

υπογραφή

η οπτική του σώματος

χρόνος εκτέλεσης

πληθικότητα, ονομαστικές παράμετροι

prototype

πώς αναλύει ο αναλυτής

κατά τη μεταγλώττιση

ομοιώματα DSL

Μια sub μπορεί να έχει και τα δύο, με το πρωτότυπο γραμμένο ως γνώρισμα:

use feature 'signatures';
sub mygrep :prototype(&@) ($code, @list) {
    grep { $code->($_) } @list;
}

mygrep { /foo/ } @items;         # prototype lets us drop `sub`
                                 # signature gives us $code and @list

Το πρωτότυπο διαμορφώνει το σημείο κλήσης· η υπογραφή διαμορφώνει το σώμα. Δεν αλληλοεπικαλύπτονται.

Ο σημαντικότερος λόγος μετάβασης#

Ανίχνευση κλήσεων λάθος πληθικότητας τη στιγμή που συμβαίνουν - όχι αρκετές γραμμές μετά, όταν τελικά κάνετε $y + 1 και προκαλείτε προειδοποίηση για τιμή undef. Οι υπογραφές μετατρέπουν μια κατηγορία λεπτών σφαλμάτων σε ξεκάθαρο μήνυμα σφάλματος.

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

  • Ορίσματα και @_ - ο κλασικός μηχανισμός με τον οποίο συνυπάρχουν οι υπογραφές.

  • Πρωτότυπα - διαφορετικός μηχανισμός, που συχνά μπερδεύεται με τις υπογραφές.

  • sub - η σελίδα perlfunc της δεσμευμένης λέξης.

  • Δήλωση - πού βρίσκονται οι υπογραφές στη σύνταξη δήλωσης.

  • Εμβέλεια - οι παράμετροι υπογραφής είναι λεξιλογικές τύπου my.