Πίνακες με tie#

Ένας πίνακας με tie έχει δύο στρώματα διεπαφής. Το κατώτερο στρώμα - FETCH, STORE, FETCHSIZE, STORESIZE, CLEAR - είναι υποχρεωτικό και χειρίζεται την προσπέλαση στοιχείων και την αλλαγή μεγέθους. Το ανώτερο στρώμα - PUSH, POP, SHIFT, UNSHIFT, SPLICE, DELETE, EXISTS - είναι προαιρετικό και επιτρέπει στον πίνακα να ανταποκρίνεται στις αντίστοιχες ενσωματωμένες· χωρίς ένα συγκεκριμένο hook, η αντίστοιχη ενσωματωμένη είτε καταφεύγει στο κατώτερο στρώμα είτε πεθαίνει.

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

Μια πλήρης κλάση#

Ένας αριθμητικός πίνακας του οποίου οι κενές θυρίδες διαβάζονται ως 0 αντί για undef, και του οποίου το STORESIZE γεμίζει τις νέες θυρίδες με 0:

use v5.36;

package ZeroFill {
    sub TIEARRAY  { my $class = shift; return bless { data => [] }, $class }
    sub FETCH     { my ($self, $i) = @_; return $self->{data}[$i] // 0 }
    sub STORE     { my ($self, $i, $v) = @_; $self->{data}[$i] = $v }
    sub FETCHSIZE { my $self = shift; return scalar @{ $self->{data} } }
    sub STORESIZE {
        my ($self, $n) = @_;
        my $data = $self->{data};
        if ($n > @$data) {
            push @$data, (0) x ($n - @$data);   # pad with 0, not undef
        } else {
            $#$data = $n - 1;
        }
    }
    sub CLEAR { my $self = shift; $self->{data} = [] }
    sub PUSH  { my $self = shift; push @{ $self->{data} }, @_; return scalar @{ $self->{data} } }
    sub POP   { my $self = shift; return pop @{ $self->{data} } }
}

tie my @a, 'ZeroFill';
$a[0] = 11;
$a[2] = 33;                # FETCH on the skipped slot 1 reads back 0
say scalar @a;             # 3
say $a[1];                 # 0
$#a = 4;                   # STORESIZE grows the array, padding with 0
say "@a";                  # 11 0 33 0 0
push @a, 44;
say pop @a;                # 44

Το αντικείμενο είναι μια blessed αναφορά hash που περιτυλίγει έναν πραγματικό πίνακα στη θυρίδα data της - ένα συνηθισμένο πρότυπο, επειδή η κλάση συνήθως θέλει χώρο για κατάσταση πέρα από τον ίδιο τον πίνακα.

Το υποχρεωτικό στρώμα#

TIEARRAY classname, LIST

Ο κατασκευαστής· επιστρέφει μια blessed αναφορά που γίνεται το αντικείμενο με tie.

FETCH this, index / STORE this, index, value

Ανάγνωση και εγγραφή ενός μόνο στοιχείου. Ένας αρνητικός δείκτης μεταφράζεται σε θετικό - ο χρόνος εκτέλεσης καλεί πρώτα το FETCHSIZE και το προσθέτει στον δείκτη - εκτός αν η κλάση θέσει το $NEGATIVE_INDICES σε αληθή τιμή, οπότε ο ακατέργαστος αρνητικός δείκτης φτάνει στα FETCH και STORE.

FETCHSIZE this

Επιστρέφει τον αριθμό των στοιχείων. Αυτό στηρίζει το scalar @array και το ανώτατο όριο για το $#array. Είναι η μεμονωμένα πιο συχνά καλούμενη μέθοδος σε έναν πίνακα με tie, καθώς ο χρόνος εκτέλεσης χρειάζεται το μέγεθος για να κανονικοποιήσει τους αρνητικούς δείκτες και να οριοθετήσει την επανάληψη.

STORESIZE this, count

Ορίζει το μήκος του πίνακα. Η μεγέθυνση πρέπει να γεμίζει τις νέες θυρίδες με την έννοια του «κενού» της κλάσης (εδώ, 0)· η συρρίκνωση πρέπει να αφαιρεί το πλεόνασμα. Αυτό στηρίζει το $#array = N και την αλλαγή μεγέθους που οδηγείται από ανάθεση.

CLEAR this

Αδειάζει τον πίνακα - πυροδοτείται από το @array = ().

Το πρωτόκολλο μεγέθους στην πράξη#

Τα FETCHSIZE και STORESIZE είναι το σημείο όπου οι πίνακες με tie διαφέρουν περισσότερο από τα hash με tie. Τρία πράγματα να έχετε ξεκάθαρα:

  • Το $#a διαβάζει μέσω του FETCHSIZE και γράφει μέσω του STORESIZE. Η ρύθμιση $#a = 4 καλεί STORESIZE($obj, 5).

  • Οι θυρίδες που παραλείπονται είναι πρόβλημα της κλάσης. Η ανάθεση στο $a[2] όταν ο πίνακας έχει μήκος 1 καλεί απευθείας STORE($obj, 2, ...) - ο χρόνος εκτέλεσης δεν καλεί το STORESIZE για να γεμίσει το κενό. Αν μια ανάγνωση της θυρίδας 1 πρέπει να επιστρέφει 0 αντί για undef, το FETCH πρέπει να το πει (το // 0 στο παράδειγμα).

  • Το EXTEND είναι συμβουλευτικό. Όταν ο χρόνος εκτέλεσης αναμένει ότι ένας πίνακας θα μεγαλώσει

    • για παράδειγμα ακριβώς πριν από μια μεγάλη ανάθεση - μπορεί να καλέσει EXTEND this, count ως υπόδειξη για προδέσμευση. Είναι πάντα ασφαλές να αφήσετε το EXTEND αόριστο ή κενό· είναι καθαρά μια ευκαιρία βελτιστοποίησης, και το να το κάνετε να συμπεριφέρεται σαν STORESIZE είναι σφάλμα, επειδή ο χρόνος εκτέλεσης καλεί το EXTEND σε στιγμές που δεν θέλει στην πραγματικότητα να αλλάξει το ορατό μήκος.

Οι προαιρετικοί μεταλλάκτες#

Καθένα από τα push, pop, shift, unshift, splice, delete και exists δρομολογεί στο ομώνυμο hook με κεφαλαία όταν η κλάση το ορίζει. Ο χρονισμός κλήσεών τους, επιβεβαιωμένος έναντι του διερμηνευτή:

  • push @a, 1, 2 → μία κλήση PUSH με ολόκληρη τη λίστα.

  • pop @aPOP· shift @aSHIFT· unshift @a, 0UNSHIFT.

  • splice @a, 1, 1, 'x', 'y' → μία κλήση SPLICE που μεταφέρει το offset, το μήκος και τη λίστα αντικατάστασης· η τιμή επιστροφής της γίνεται η τιμή του splice.

  • exists $a[0]EXISTS· delete $a[0]DELETE.

Όταν ένα hook μεταλλάκτη απουσιάζει, η συμπεριφορά εξαρτάται από τον τελεστή. Το Tie::Array (παρακάτω) παρέχει εφεδρικές υλοποιήσεις για τα συνηθισμένα πέντε με όρους του υποχρεωτικού στρώματος· χωρίς εκείνη τη βασική κλάση, η κλήση ενός μη υλοποιημένου μεταλλάκτη πεθαίνει στο σημείο χρήσης.

Χρονισμός κλήσεων#

  • $a[$i]FETCH· $a[$i] = $vSTORE.

  • scalar @a, κανονικοποίηση αρνητικού δείκτη → FETCHSIZE.

  • $#a = $n, αλλαγή μεγέθους @a = (...)STORESIZE (και πρώτα CLEAR για ανάθεση ολόκληρου του πίνακα).

  • push/pop/shift/unshift/splice/delete/exists → το αντίστοιχο hook με κεφαλαία όταν είναι ορισμένο.

  • Μια φέτα πίνακα @a[1, 3] καλεί το FETCH μία φορά ανά δείκτη· δεν υπάρχει hook για φέτες.

Ελάχιστη βιώσιμη κλάση#

Ο μικρότερος πίνακας με tie ορίζει τα TIEARRAY, FETCH, STORE, FETCHSIZE και STORESIZE. Με αυτά τα πέντε, η προσπέλαση στοιχείων, το scalar @a, το $#a και η επανάληψη λειτουργούν όλα. Προσθέστε το CLEAR ώστε το @a = () να συμπεριφέρεται σωστά. Προσθέστε τους μεταλλάκτες μόνο για τις ενσωματωμένες που πραγματικά χρησιμοποιούν οι καλούντες - ή κληρονομήστε τους.

Ο εύκολος τρόπος: κληρονομήστε από το Tie::Array#

Το Tie::Array υλοποιεί τα PUSH, POP, SHIFT, UNSHIFT και SPLICE με όρους των υποχρεωτικών πέντε, οπότε μια υποκλάση που παρέχει τα TIEARRAY, FETCH, STORE, FETCHSIZE και STORESIZE αποκτά όλους τους μεταλλάκτες λίστας δωρεάν:

package Logging {
    use Tie::Array;
    our @ISA = ('Tie::Array');
    sub TIEARRAY  { my $class = shift; return bless { data => [] }, $class }
    sub FETCH     { my ($self, $i) = @_; return $self->{data}[$i] }
    sub STORE     { my ($self, $i, $v) = @_; warn "store $i\n"; $self->{data}[$i] = $v }
    sub FETCHSIZE { my $self = shift; return scalar @{ $self->{data} } }
    sub STORESIZE { my ($self, $n) = @_; $#{ $self->{data} } = $n - 1 }
}

tie my @log, 'Logging';
push @log, 'x', 'y';        # PUSH inherited; calls STORE twice (warns)
say "@log";                # x y

Το push @log, 'x', 'y' λειτουργεί χωρίς μέθοδο PUSH επειδή η κληρονομημένη αναπτύσσεται σε δύο κλήσεις STORE - γι” αυτό το παράδειγμα προειδοποιεί δύο φορές. Το Tie::Array δεν ορίζει το ίδιο το TIEARRAY, οπότε η υποκλάση πρέπει να παρέχει τον κατασκευαστή· τα προεπιλεγμένα του DELETE και EXISTS απλώς κράζουν, οπότε υπερκαλύψτε τα αν οι καλούντές σας χρειάζονται τα delete και exists.

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

  • tie - η ενσωματωμένη και το πλήρες μενού μεθόδων πίνακα

  • tied - προσεγγίστε το αντικείμενο που στηρίζει

  • splice - ο πιο περίπλοκος μεταλλάκτης που αντιπροσωπεύει το hook SPLICE

  • push και pop - οι μεταλλάκτες που το Tie::Array παράγει από το υποχρεωτικό στρώμα

  • Hash με tie - η άλλη πλούσια διεπαφή περιέκτη

  • Tie::Array - το άρθρωμα της βασικής κλάσης που παρέχει τους μεταλλάκτες λίστας