# Πίνακες με tie Ένας πίνακας με tie έχει δύο στρώματα διεπαφής. Το κατώτερο στρώμα - `FETCH`, `STORE`, `FETCHSIZE`, `STORESIZE`, `CLEAR` - είναι υποχρεωτικό και χειρίζεται την προσπέλαση στοιχείων και την αλλαγή μεγέθους. Το ανώτερο στρώμα - `PUSH`, `POP`, `SHIFT`, `UNSHIFT`, `SPLICE`, `DELETE`, `EXISTS` - είναι προαιρετικό και επιτρέπει στον πίνακα να ανταποκρίνεται στις αντίστοιχες ενσωματωμένες· χωρίς ένα συγκεκριμένο hook, η αντίστοιχη ενσωματωμένη είτε καταφεύγει στο κατώτερο στρώμα είτε πεθαίνει. Το πλήρες μενού βρίσκεται στη σελίδα αναφοράς του [`tie`](../../p5/core/perlfunc/tie.md). Αυτό το κεφάλαιο φτιάχνει μια κλάση, και έπειτα εξηγεί το πρωτόκολλο μεγέθους και τους προαιρετικούς μεταλλάκτες, με τον χρονισμό κλήσεών τους επαληθευμένο έναντι του εκτελούμενου διερμηνευτή. ## Μια πλήρης κλάση Ένας αριθμητικός πίνακας του οποίου οι κενές θυρίδες διαβάζονται ως `0` αντί για `undef`, και του οποίου το `STORESIZE` γεμίζει τις νέες θυρίδες με `0`: ```perl 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`](../../p5/core/perlfunc/push.md), [`pop`](../../p5/core/perlfunc/pop.md), [`shift`](../../p5/core/perlfunc/shift.md), [`unshift`](../../p5/core/perlfunc/unshift.md), [`splice`](../../p5/core/perlfunc/splice.md), [`delete`](../../p5/core/perlfunc/delete.md) και [`exists`](../../p5/core/perlfunc/exists.md) δρομολογεί στο ομώνυμο 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` αποκτά όλους τους μεταλλάκτες λίστας δωρεάν: ```perl 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`](../../p5/core/perlfunc/delete.md) και [`exists`](../../p5/core/perlfunc/exists.md). ## Δείτε επίσης - [`tie`](../../p5/core/perlfunc/tie.md) - η ενσωματωμένη και το πλήρες μενού μεθόδων πίνακα - [`tied`](../../p5/core/perlfunc/tied.md) - προσεγγίστε το αντικείμενο που στηρίζει - [`splice`](../../p5/core/perlfunc/splice.md) - ο πιο περίπλοκος μεταλλάκτης που αντιπροσωπεύει το hook `SPLICE` - [`push`](../../p5/core/perlfunc/push.md) και [`pop`](../../p5/core/perlfunc/pop.md) - οι μεταλλάκτες που το `Tie::Array` παράγει από το υποχρεωτικό στρώμα - [Hash με tie](hashes.md) - η άλλη πλούσια διεπαφή περιέκτη - `Tie::Array` - το άρθρωμα της βασικής κλάσης που παρέχει τους μεταλλάκτες λίστας