Γνωρίσματα#

Ένα γνώρισμα sub είναι ένας σχολιασμός με πρόθεμα άνω-κάτω τελεία ανάμεσα στο όνομα (ή την υπογραφή) της sub και στο σώμα της. Τα γνωρίσματα σημειώνουν μια sub ως έχουσα κάποια ειδική ιδιότητα - ότι είναι μέθοδος, ότι είναι lvalue, ότι έχει πρωτότυπο, ότι είναι σταθερά, ότι υπόκειται σε κάποιον μηχανισμό τρίτων - πάνω στον οποίο μπορεί να δράσει ο χρόνος εκτέλεσης, ο αναλυτής, ή ένας χειριστής γνωρισμάτων γραμμένος από τον χρήστη.

sub greet :method ($name)            { ... }
sub editable :lvalue                 { ... }
sub mypush :prototype(\@@)           { ... }
sub PI :const                        { 3.14159 }
sub handler :Memoize                 { ... }   # third-party attribute

Τα τέσσερα βήματα γνωρισμάτων#

Για να παρακολουθήσετε πώς ένα γνώρισμα επηρεάζει μια sub, διακρίνετε τέσσερις φάσεις:

  1. Ανάλυση. Ο μεταγλωττιστής διαβάζει :foo(args) και το χωρίζει σε όνομα (foo) και προαιρετική συμβολοσειρά παραμέτρων (args).

  2. Καταχώρηση. Ο μεταγλωττιστής καλεί είτε έναν ενσωματωμένο χειριστή (για :lvalue, :method, :prototype(...)) είτε τη MODIFY_CODE_ATTRIBUTES από το σχετικό πακέτο (για γνωρίσματα ορισμένα από τον χρήστη).

  3. Αποθήκευση. Τα ενσωματωμένα γνωρίσματα θέτουν μια σημαία στο CV (το μεταγλωττισμένο αντικείμενο sub). Τα γνωρίσματα χρήστη αποθηκεύονται όπου επιλέξει ο χειριστής.

  4. Ερώτημα. Οποιοσδήποτε μπορεί αργότερα να ρωτήσει attributes::get(\&sub) για τη λίστα γνωρισμάτων. Τα ενσωματωμένα γνωρίσματα μπορούν επίσης να ερωτηθούν μέσω πιο άμεσων API (prototype, is_lvalue, …).

Οι τέσσερις φάσεις εξηγούν γιατί τα γνωρίσματα είναι ετερογενή σε συμπεριφορά: τα ενσωματωμένα γνωρίσματα είναι έννοιες χρόνου εκτέλεσης· τα γνωρίσματα χρήστη είναι ό,τι αποφασίσει ο κώδικας στη MODIFY_CODE_ATTRIBUTES να κάνει μαζί τους.

Ενσωματωμένα γνωρίσματα#

:lvalue#

Σημαίνει τη sub ως χρησιμοποιήσιμη στην αριστερή πλευρά μιας ανάθεσης. Η τελευταία έκφραση του σώματος πρέπει να είναι lvalue. Δείτε lvalue και περιβάλλον.

sub editable :lvalue { $self->{x} }

$obj->editable = 42;             # writes through to $self->{x}

:method#

Υπόδειξη στον αναλυτή και στην ενδοσκόπηση ότι αυτή η sub προορίζεται να κληθεί ως μέθοδος. Η σημαία είναι κυρίως ενημερωτική· καταστέλλει επίσης τα αποτελέσματα πρωτοτύπων (αφού οι μέθοδοι δεν τηρούν τα πρωτότυπα ούτως ή άλλως):

package Counter;

sub increment :method {
    my $self = shift;
    $self->{n}++;
}

Η :method δεν κάνει αυτόματα use parent ούτε θέτει @ISA· δεν αλλάζει την αποστολή. Είναι δείκτης τεκμηρίωσης / πρόθεσης που ο χρόνος εκτέλεσης χρησιμοποιεί επίσης για να παρακάμψει την επεξεργασία πρωτοτύπων.

:prototype(...)#

Η μορφή γνωρίσματος ενός πρωτοτύπου, απαιτείται όταν οι υπογραφές είναι ενεργοποιημένες στην ίδια εμβέλεια:

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

Η λίστα παραμέτρων του σώματος είναι η υπογραφή· το γνώρισμα φέρει το πρωτότυπο. Χωρίς τη μορφή γνωρίσματος, ο αναλυτής δεν μπορεί να ξεχωρίσει ποια λίστα σε παρενθέσεις είναι ποια.

:const#

Υπόσχεται στον χρόνο εκτέλεσης ότι το σώμα της sub παράγει σταθερό αποτέλεσμα, επιτρέποντας πιο επιθετική ενσωμάτωση από ό,τι δίνει το σκέτο πρωτότυπο ():

sub PI :const { 3.14159265358979 }

Η :const εισήχθη για χρήση από τη use constant και παρόμοια εργαλεία. Ο περισσότερος κώδικας πρέπει να χρησιμοποιεί τη use constant αντί να εφαρμόζει απευθείας :const.

Γνωρίσματα τρίτων#

Οποιοδήποτε πακέτο μπορεί να καταχωρήσει χειριστές γνωρισμάτων για αναφορές κώδικα υλοποιώντας τη MODIFY_CODE_ATTRIBUTES. Το κλασικό παράδειγμα είναι η Attribute::Handlers, που παρέχει ένα πιο δηλωτικό API πάνω στον ωμό μηχανισμό. Ένα γνώρισμα χρήστη μοιάζει πανομοιότυπο με ενσωματωμένο στο σημείο κλήσης:

use Some::Memoize;               # provides ':Memoize' attribute

sub expensive :Memoize {
    ...
}

Το πακέτο που παρέχει το γνώρισμα αποφασίζει τι σημαίνει η :Memoize. Συνηθισμένα γνωρίσματα τρίτων:

  • :Memoize - αυτόματη αποθήκευση τιμών επιστροφής σε cache (από τη Memoize).

  • :Logged - περιτύλιγμα της sub με καταγραφή εισόδου/εξόδου (από διάφορα αρθρώματα τύπου AOP).

  • :Test, :Benchmark - δείκτες πλαισίου δοκιμών.

Όταν βλέπετε ένα γνώρισμα-με-άνω-κάτω-τελεία που δεν βρίσκεται στην παραπάνω λίστα ενσωματωμένων, βρείτε το πακέτο που το παρέχει.

Πώς γράφονται τα γνωρίσματα#

Πολλαπλά γνωρίσματα μπορούν να αλυσιδωθούν. Η σειρά στο δίσκο είναι η σειρά επεξεργασίας τους:

sub fancy :method :prototype(\@) :Memoize {
    ...
}

Τα γνωρίσματα προσαρτώνται τόσο στην προδήλωση όσο και στον ορισμό· αν γράψετε και τα δύο, οι λίστες γνωρισμάτων πρέπει να συμφωνούν:

sub greet :method;               # forward declaration with :method
sub greet :method ($name) {      # body, same attribute
    ...
}

Η υπογραφή, όταν υπάρχει, έρχεται μετά τα γνωρίσματα:

sub greet :method ($name) {      # OK
    ...
}

sub greet ($name) :method {      # error: attributes must precede signature
    ...
}

Τι δεν κάνουν τα γνωρίσματα#

  • Δεν είναι σχολιασμοί τύπου. Η :Int δεν είναι ενσωματωμένη· προσπάθειες ανάγνωσής της με αυτόν τον τρόπο δεν θα κάνουν τίποτα εκτός αν κάποιο πακέτο έχει καταχωρήσει χειριστή που σημαίνει το ίδιο πράγμα.

  • Δεν είναι μεταδεδομένα για το πλαίσιο δοκιμών εκτός αν το πλαίσιο δοκιμών κατέχωρησε χειριστή γι” αυτά. Το σκόρπισμα :tested σε subs δεν κάνει τίποτα χωρίς κώδικα στην άλλη πλευρά που να διαβάζει τον σχολιασμό.

  • Δεν είναι οδηγίες βελτιστοποίησης. Η :fast δεν κάνει τίποτα.

Ερώτημα γνωρισμάτων μιας sub#

use attributes;

sub demo :method :prototype($) :Memoize { ... }

my @attrs = attributes::get(\&demo);
# returns: ('method', 'prototype($)', 'Memoize')

Αυτό είναι το ισοδύναμο ενδοσκόπησης της prototype - απαντάει στο «ποιους σχολιασμούς φέρει αυτή η sub;» αντί στο «πώς μοιάζει αυτή η sub στον αναλυτή;».

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