state#
Δηλώνει μια λεξιλογικά οριοθετημένη μεταβλητή της οποίας η τιμή επιμένει μεταξύ κλήσεων της περικλείουσας υπορουτίνας.
Μια μεταβλητή state έχει τους ίδιους κανόνες ορατότητας με μια μεταβλητή my - είναι ορατή μόνο μέσα στο μπλοκ που τη δηλώνει - αλλά αρχικοποιείται ακριβώς μία φορά ανά περικλείον closure, όχι σε κάθε είσοδο στο μπλοκ. Αυτός είναι ο ενσωματωμένος τρόπος της Perl να γράφετε επίμονες ιδιωτικές μεταβλητές: μετρητές, μνήμες memoisation, πίνακες αναζήτησης που υπολογίζονται μία φορά, οτιδήποτε διαφορετικά θα κρύβατε σε λεξιλογική σε εμβέλεια αρχείου πάνω από μια sub.
Σύνοψη#
state $scalar
state $scalar = EXPR
state ($a, $b, undef, $c)
state @array
state %hash
state TYPE $var
state $var : ATTRS
Τι επιστρέφεται#
Η state είναι δήλωση, όχι συνάρτηση. Όπως η my, επιστρέφει την/τις μεταβλητή/ές που δηλώνει, οπότε μπορεί να χρησιμοποιηθεί σε οποιαδήποτε θέση έκφρασης όπου θα εμφανιζόταν η δηλωμένη μεταβλητή:
(state $n //= 0)++; # declare-and-use in one expression
print ++(state $calls), "\n"; # count calls to this line
Ο αρχικοποιητής στη δεξιά πλευρά του = αποτιμάται μόνο την πρώτη φορά που ο έλεγχος περνά μέσα από τη δήλωση σε εκείνο το συγκεκριμένο στιγμιότυπο closure. Σε κάθε επόμενη είσοδο, η δήλωση επανεκθέτει την ήδη αρχικοποιημένη μεταβλητή και ο αρχικοποιητής παραλείπεται.
Εμβέλεια επιμονής - τι σημαίνει «μία φορά»#
«Μία φορά ανά περικλείον closure» είναι ο ακριβής κανόνας. Μια μεταβλητή state ζει όσο το CV (code value) που περιέχει τη δήλωσή της:
Σε μια ονομασμένη υπορουτίνα, το CV δημιουργείται μία φορά κατά τη μεταγλώττιση. Η μεταβλητή
stateαρχικοποιείται στην πρώτη κλήση και διατηρεί την τιμή της σε κάθε επόμενη κλήση για όλη τη διάρκεια ζωής της διεργασίας.Σε μια ανώνυμη υπορουτίνα / closure, κάθε αποτίμηση
sub { ... }παράγει ένα φρέσκο CV με τη δική του φρέσκια θέσηstate. Δύο closures φτιαγμένα από την ίδια πηγή δεν μοιράζονται κατάσταση.Σε ένα εμφωλευμένο μπλοκ μέσα σε υπορουτίνα, το
stateεξακολουθεί να ανήκει στο περιβάλλον CV - δεν αναδημιουργείται σε κάθε είσοδο στο εσωτερικό μπλοκ.
sub counter { state $n = 0; ++$n }
counter(); counter(); counter(); # returns 1, 2, 3
my $make = sub { sub { state $n = 0; ++$n } };
my $a = $make->();
my $b = $make->();
$a->(); $a->(); $b->(); # $a sees 1, 2; $b sees 1
Πύλη χαρακτηριστικού και διαθεσιμότητα#
Η state ήταν ανέκαθεν υπό feature-gate. Δύο τρόποι για να ενεργοποιηθεί:
use feature 'state';- ενεργοποιεί μόνο αυτό το χαρακτηριστικό.use v5.10;(ή οποιοδήποτε νεότερο πακέτο εκδόσεων, συμπεριλαμβανομένων τωνuse v5.36καιuse v5.42) - έμμεσα μέσω του πακέτου χαρακτηριστικών της έκδοσης. Κάθε σύγχρονο πρόγραμμα έχει ήδη διαθέσιμη τηstateμέσω της γραμμήςuse vX.Yτου.
Χωρίς το χαρακτηριστικό σε εμβέλεια, η state αναλύεται ως κανονικό bareword. Για να φτάσετε την ενσωματωμένη ανεξάρτητα από αυτό, γράψτε CORE::state.
use v5.36; # 'state' feature is on by default
sub hits { state $n = 0; ++$n }
Δηλώσεις λίστας και αρχικοποιητές#
Η state υποστηρίζει τη μορφή λίστας με παρενθέσεις για τη δήλωση πολλών μεταβλητών ταυτόχρονα, με undef ως σύμβολο κράτησης θέσης:
state ($x, $y, undef, $z);
Αυτό που δεν υποστηρίζει ακόμη είναι η αρχικοποίηση λίστας μεταβλητών state. Το upstream το σημειώνει ρητά: η αρχικοποίηση μεταβλητών state σε λίστα με παρενθέσεις δεν είναι προς το παρόν εφικτή, οπότε η μορφή λίστας είναι χρήσιμη μόνο για δήλωση.
Τα συσσωματώματα - state @array και state %hash - είχαν ιστορικά τον ίδιο περιορισμό σε αρχικοποιητές (η Perl εξέπεμπε πειραματική προειδοποίηση, και σε αρκετές εκδόσεις συντακτικό σφάλμα, για state @a = (1, 2, 3)). Χτίστε το συσσωμάτωμα μέσα στο σώμα αντ” αυτού:
sub primes_below_100 {
state @p;
unless (@p) {
@p = _sieve(100); # fill on first call only
}
return @p;
}
Το ιδίωμα unless (@p) / unless (%h) παραπάνω είναι ο φορητός αντικαταστάτης ενός αρχικοποιητή συσσωματώματος.
Θέση δήλωσης και σημασιολογία της ίδιας εντολής#
Όπως οι my, our και local, μια δήλωση state μπορεί να εμφανιστεί όπου επιτρέπεται έκφραση (εκτός από την παρεμβολή συμβολοσειρών). Η νέα σύνδεση τίθεται σε ισχύ για τις επόμενες εντολές, όχι για άλλες αναφορές στην ίδια μεταβλητή στην ίδια εντολή:
package main;
use feature 'state';
our $x = 2;
foo($x, state $x = $x + 1, $x); # foo() receives (2, 3, 2)
foo($x, $main::x); # foo() receives (3, 2)
Η πρώτη $x και η τελική $x στην ίδια κλήση εξακολουθούν να αναφέρονται στην our $x· μόνο η επόμενη εντολή βλέπει την state $x.
Παραδείγματα#
Μετρητής επισκέψεων - το καθιερωμένο μονόγραμμο:
sub hits { state $n = 0; ++$n }
hits(); hits(); hits(); # returns 1, 2, 3
Memoisation ακριβής καθαρής συνάρτησης:
sub fib {
my ($n) = @_;
state %cache;
return $cache{$n} //= $n < 2 ? $n : fib($n-1) + fib($n-2);
}
Οκνηρή εφάπαξ εγκατάσταση πίνακα μόνο-για-ανάγνωση:
sub months {
state @m = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
return @m;
}
Εδώ ο αρχικοποιητής είναι μια απλή λίστα, που λειτουργεί επειδή η δεξιά πλευρά του state @m = ... αναδιπλώνεται σε σταθερή λίστα και η τρέχουσα Perl επιτρέπει αυτή τη μορφή· η πιο γενική περίπτωση υπολογισμένης λίστας εξακολουθεί να χρειάζεται το μοτίβο unless (@m) { ... } παραπάνω για φορητότητα.
Κατάσταση ανά closure (φρέσκια θέση ανά αποτίμηση sub { ... }):
sub make_counter {
return sub { state $n = 0; ++$n };
}
my $a = make_counter();
my $b = make_counter();
$a->(); $a->(); # 1, 2
$b->(); # 1 - independent of $a
Δήλωση και χρήση σε μία και μόνη έκφραση:
while (my $line = <$fh>) {
warn "first line was: $line" if (state $first //= $line) eq $line;
}
Οριακές περιπτώσεις#
Αρχικοποιητές συσσωματωμάτων. Τα
state @a = (1, 2, 3)καιstate %h = (k => 1)έχουν μακρά ιστορία ως περιορισμένα ή πειραματικά. Όταν αμφιβάλλετε, δηλώστε το συσσωμάτωμα και γεμίστε το υπόunless (@a) { ... }στην πρώτη κλήση. Οι αρχικοποιητές βαθμωτών (state $x = EXPR) ήταν πάντοτε εντάξει.Επαναχρησιμοποίηση στην ίδια εντολή. Μια φρέσκια δήλωση
stateδεν ισχύει για άλλες αναφορές στο ίδιο όνομα μέσα στην ίδια εντολή· εκείνες εξακολουθούν να επιλύονται σε ό,τι ήταν σε εμβέλεια πριν τη δήλωση. Δείτε το παράδειγμαfoo($x, state $x = $x + 1, $x)παραπάνω.Προειδοποίηση επικάλυψης. Η εκ νέου δήλωση του ίδιου ονόματος στην ίδια εμβέλεια -
state $n; state $n;- επικαλύπτει την πρώτη. Υπόuse warningsεκπέμπεται προειδοποίηση στην κατηγορίαshadow. Σχεδόν πάντα σφάλμα.Πολλαπλότητα ανώνυμης υπορουτίνας. Κάθε αποτίμηση
sub { ... }παράγει νέο CV με τη δική του θέσηstate. Αν χτίζετε πολλά closures από την ίδια πηγή, καθένα έχει τη δική του ανεξάρτητηstate- αυτό είναι χαρακτηριστικό, αλλά εκπλήσσει όσους περιμένουν επιμονή σε εμβέλεια αρχείου.Νήματα και forks. Η
stateζει στο CV, το οποίο κλωνοποιείται στοfork(copy-on-write με την έννοια του ΛΣ - κάθε διεργασία συνεχίζει με το δικό της αντίγραφο) και στοthreads->create(βαθιά κλωνοποίηση). Δύο διεργασίες ή νήματα δεν μοιράζονται έναν μετρητήstate.Η
localδεν ισχύει. Οι μεταβλητέςstateείναι λεξιλογικές, όχι μεταβλητές πακέτου, οπότε ηlocalδεν μπορεί να τις αποθηκεύσει και να τις επαναφέρει. Τυλίξτε ολόκληρη την υπορουτίνα στη δική της εμβέλεια ή χρησιμοποιήστε διαφορετικό μηχανισμό αν χρειάζεστε δυναμική εμβέλεια.Προσπέλαση μέσω
CORE::state. Όταν γράφετε κώδικα που πρέπει να αναλύεται ακόμη και χωρίς το χαρακτηριστικό ενεργοποιημένο, προθέστε τη δεσμευμένη λέξη:CORE::state $n = 0;.Καταστροφή. Οι μεταβλητές
stateζουν όσο το CV. Για ονομασμένη υπορουτίνα στο κύριο πρόγραμμα αυτό σημαίνει «μέχρι την καθολική καταστροφή». Μη βάζετε filehandle ή μεγάλο buffer σε μεταβλητήstateπεριμένοντας να απελευθερωθεί κατά την έξοδο της υπορουτίνας - δεν θα απελευθερωθεί.
Διαφορές από το upstream#
Πλήρως συμβατό με το upstream Perl 5.42.
Δείτε επίσης#
my- συνήθης λεξιλογική· επαναρχικοποιείται σε κάθε είσοδο μπλοκ, η φυσική αντίθεση στηstateour- λεξιλογικό ψευδώνυμο μεταβλητής πακέτου· χρησιμοποιήστε όταν οι καλούντες χρειάζεται να δουν ή να εφαρμόσουνlocalστην τιμήlocal- δυναμική εμβέλεια για μεταβλητές πακέτου· ορθογώνια προς τηstateκαι δεν μπορεί να εφαρμοστεί σε αυτήνsub- ηstateζει μέσα σε CV· η κατανόηση των διαφορών μεταξύ ονομασμένων και ανώνυμων υπορουτινών εξηγεί την κατάσταση ανά closureuse- ηuse v5.10και νεότερες ενεργοποιούν το χαρακτηριστικόstateως μέρος του πακέτου της έκδοσηςundef- νόμιμο σύμβολο κράτησης θέσης μέσα σε δηλώσεις λίσταςstate (...)