Εμβέλεια

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 - συνήθης λεξιλογική· επαναρχικοποιείται σε κάθε είσοδο μπλοκ, η φυσική αντίθεση στη state

  • our - λεξιλογικό ψευδώνυμο μεταβλητής πακέτου· χρησιμοποιήστε όταν οι καλούντες χρειάζεται να δουν ή να εφαρμόσουν local στην τιμή

  • local - δυναμική εμβέλεια για μεταβλητές πακέτου· ορθογώνια προς τη state και δεν μπορεί να εφαρμοστεί σε αυτήν

  • sub - η state ζει μέσα σε CV· η κατανόηση των διαφορών μεταξύ ονομασμένων και ανώνυμων υπορουτινών εξηγεί την κατάσταση ανά closure

  • use - η use v5.10 και νεότερες ενεργοποιούν το χαρακτηριστικό state ως μέρος του πακέτου της έκδοσης

  • undef - νόμιμο σύμβολο κράτησης θέσης μέσα σε δηλώσεις λίστας state (...)