Πίνακες με 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 @a→POP·shift @a→SHIFT·unshift @a, 0→UNSHIFT.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] = $v→STORE.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- ο πιο περίπλοκος μεταλλάκτης που αντιπροσωπεύει το hookSPLICEpushκαιpop- οι μεταλλάκτες που τοTie::Arrayπαράγει από το υποχρεωτικό στρώμαHash με tie - η άλλη πλούσια διεπαφή περιέκτη
Tie::Array- το άρθρωμα της βασικής κλάσης που παρέχει τους μεταλλάκτες λίστας