Hash με tie#
Ένα hash με tie είναι ο πλουσιότερος από τους τύπους tie, επειδή ένα hash κάνει περισσότερα από το να διαβάζει και να γράφει μεμονωμένα στοιχεία - επαναλαμβάνει, αναφέρει το μέγεθός του, ελέγχει την ύπαρξη κλειδιών, και καθαρίζει. Καθένα από αυτά είναι ένα ξεχωριστό hook. Τα hash ήταν ο πρώτος τύπος δεδομένων της Perl που μπορούσε να δεθεί με tie (η αρχική περίπτωση χρήσης ήταν το δέσιμο ενός %hash σε ένα αρχείο DBM στον δίσκο), και η διεπαφή ακόμη αντανακλά αυτή την κληρονομιά.
Το πλήρες μενού μεθόδων βρίσκεται στη σελίδα αναφοράς του tie. Αυτό το κεφάλαιο φτιάχνει μία κλάση και εξηγεί κάθε hook, με προσοχή στο μέρος που μπερδεύει τον κόσμο: το πρωτόκολλο επανάληψης.
Μια πλήρης κλάση#
Ένα hash του οποίου τα κλειδιά είναι χωρίς διάκριση πεζών-κεφαλαίων - τα $h{Name} και $h{NAME} αναφέρονται στην ίδια θυρίδα:
use v5.36;
package CaseInsensitive {
sub TIEHASH { my $class = shift; return bless {}, $class }
sub STORE { my ($self, $key, $val) = @_; $self->{lc $key} = $val }
sub FETCH { my ($self, $key) = @_; return $self->{lc $key} }
sub EXISTS { my ($self, $key) = @_; return exists $self->{lc $key} }
sub DELETE { my ($self, $key) = @_; return delete $self->{lc $key} }
sub CLEAR { my $self = shift; %$self = () }
sub FIRSTKEY { my $self = shift; my $reset = keys %$self; return each %$self }
sub NEXTKEY { my $self = shift; return each %$self }
sub SCALAR { my $self = shift; return scalar %$self }
}
tie my %h, 'CaseInsensitive';
$h{Name} = 'Ada';
$h{EMAIL} = 'ada@example.org';
say $h{name}; # Ada
say $h{email}; # ada@example.org
say exists $h{NAME} ? 'yes' : 'no'; # yes
say scalar %h ? 'nonempty' : 'empty'; # nonempty
for my $k (sort keys %h) {
say "$k => $h{$k}";
}
# email => ada@example.org
# name => Ada
Το αντικείμενο είναι μια blessed αναφορά hash που χρησιμοποιείται ως ο πραγματικός αποθηκευτικός χώρος· τα hooks μετατρέπουν το κλειδί σε πεζά πριν το αγγίξουν. Παρατηρήστε ότι η επανάληψη επιστρέφει τα αποθηκευμένα (πεζά) κλειδιά, που είναι ακριβώς αυτό που πρέπει να κάνει ένα hash χωρίς διάκριση πεζών-κεφαλαίων.
Το συμβόλαιο των μεθόδων#
TIEHASH classname, LISTΟ κατασκευαστής. Επιστρέφει μια blessed αναφορά - συνήθως αλλά όχι κατ” ανάγκη μια αναφορά hash - που γίνεται το αντικείμενο με tie.
FETCH this, key/STORE this, key, valueΑνάγνωση και εγγραφή ενός μόνο στοιχείου, το ίδιο ζεύγος με τα βαθμωτά αλλά με ένα όρισμα κλειδιού. Η τιμή επιστροφής του
STOREαγνοείται.EXISTS this, key/DELETE this, keyΣτηρίζουν τις ενσωματωμένες
existsκαιdelete. Η τιμή επιστροφής τουDELETEγίνεται η τιμή επιστροφής τουdelete· για να ταιριάζει με ένα απλό hash, επιστρέψτε ό,τι θα είχε επιστρέψει τοFETCHγια εκείνο το κλειδί πριν την αφαίρεσή του.CLEAR thisΠυροδοτείται όταν ολόκληρο το hash αδειάζει, τυπικά με την ανάθεση της κενής λίστας (
%h = ()). Αφαιρέστε τα πάντα.FIRSTKEY this/NEXTKEY this, lastkeyΤο πρωτόκολλο επανάληψης - δείτε παρακάτω. Μαζί οδηγούν τα
keys,valuesκαιeach.SCALAR thisΚαλείται όταν το hash αποτιμάται σε βαθμωτό ή λογικό περιβάλλον (
scalar %h,if (%h), και από την 5.28 τοkeys %hσε λογικό περιβάλλον). Επιστρέψτε μια τιμή που είναι αληθής όταν το hash δεν είναι κενό και ψευδής όταν είναι κενό.DESTROY thisκαιUNTIE thisΠροαιρετικά hooks καθαρισμού, ταυτόσημα σε ρόλο με την περίπτωση του βαθμωτού. Το
UNTIEκαλύπτεται στην παγίδα του untie παρακάτω.
Το πρωτόκολλο επανάληψης#
Αυτό είναι το μέρος της διεπαφής hash που δεν έχει ανάλογο στη διεπαφή του βαθμωτού, και το μέρος που πιο συχνά πάει στραβά.
Όταν ξεκινά μια επανάληψη keys, values ή each, ο χρόνος εκτέλεσης καλεί το FIRSTKEY για να πάρει το πρώτο κλειδί, και έπειτα το NEXTKEY επανειλημμένα, περνώντας κάθε φορά το κλειδί που επιστράφηκε προηγουμένως, μέχρι μια μέθοδος να επιστρέψει την κενή λίστα ή undef. Και τα δύο καλούνται πάντα σε βαθμωτό περιβάλλον και πρέπει να επιστρέφουν απλώς ένα κλειδί· ο χρόνος εκτέλεσης καλεί ο ίδιος το FETCH για να πάρει κάθε τιμή.
Δύο κανόνες κρατούν την επανάληψη σωστή:
Το
FIRSTKEYπρέπει να επαναφέρει κάθε εσωτερικό επαναλήπτη. Όταν ο υποκείμενος αποθηκευτικός χώρος είναι ένα πραγματικό hash, αποτιμήστε πρώτα τοkeys %$selfσε κενό ή βαθμωτό περιβάλλον - αυτό επαναφέρει τον δρομέαeachτης Perl ανά hash - και έπειτα καλέστε τοeach. Το αναλώσιμοmy $reset = keys %$selfστο παράδειγμα κάνει ακριβώς αυτό. Παραλείψτε το και ένα φρέσκοkeys %hμπορεί να συνεχίσει από όπου σταμάτησε η τελευταία επανάληψη.Σηματοδοτήστε το τέλος με την κενή λίστα. Όταν ο υποκείμενος αποθηκευτικός χώρος δεν είναι πραγματικό hash και δεν έχει δικό του
each, επιστρέψτε την κενή λίστα (ήundef) μόλις παραδώσετε το τελευταίο κλειδί.
Γιατί αξίζει να ορίσετε το SCALAR#
Χωρίς μια μέθοδο SCALAR, ο χρόνος εκτέλεσης μαντεύει την αλήθεια του hash σε λογικό περιβάλλον, και η εικασία μπορεί να είναι λανθασμένη - ιδίως, μπορεί να αναφέρει ένα hash ως μη κενό αμέσως μετά το άδειασμά του με επανειλημμένα DELETE. Αν το if (%tied_hash) έχει σημασία για τους καλούντές σας, ορίστε το SCALAR και επιστρέψτε την πραγματική απάντηση, όπως κάνει το παράδειγμα με το scalar %$self.
Χρονισμός κλήσεων#
Αναγνώσεις
$h{k}→FETCH·$h{k} = $v→STORE.exists $h{k}→EXISTS·delete $h{k}→DELETE.%h = ()→CLEAR·%h = (a => 1)→CLEARκαι μετάSTORE.keys %h/values %h/each %h→FIRSTKEYμία φορά, και μετάNEXTKEYανά βήμα. Ταkeysκαιvaluesσε περιβάλλον λίστας καλούν επίσηςFETCHγια κάθε κλειδί.scalar %h,if (%h)→SCALAR(αν είναι ορισμένο).Μια φέτα hash
@h{qw(a b)}καλεί τοFETCHμία φορά ανά κλειδί· δεν υπάρχει hook για φέτες.
Ελάχιστη βιώσιμη κλάση#
Ένα hash με tie ανάγνωσης/εγγραφής χρειάζεται τουλάχιστον τα TIEHASH, FETCH και STORE. Αν οι καλούντες πρόκειται ποτέ να το επαναλάβουν, προσθέστε τα FIRSTKEY και NEXTKEY - χωρίς αυτά, τα keys και each βλέπουν ένα κενό hash. Προσθέστε τα EXISTS και DELETE αν οι καλούντες χρησιμοποιούν εκείνες τις ενσωματωμένες· χωρίς αυτά, τα exists/delete πεθαίνουν στο σημείο χρήσης. Τα SCALAR και CLEAR ολοκληρώνουν την ορθότητα για λογικούς ελέγχους και μαζικό άδειασμα.
Ο εύκολος τρόπος: κληρονομήστε από το Tie::StdHash#
Το Tie::StdHash (που συνοδεύει το Tie::Hash) υλοποιεί ολόκληρο το μενού πάνω σε μια blessed αναφορά hash. Κληρονομήστε από αυτό και υπερκαλύψτε μόνο τα hooks που πρέπει να συμπεριφέρονται διαφορετικά:
package LoudHash {
use Tie::Hash;
our @ISA = ('Tie::StdHash');
sub STORE { my ($self, $k, $v) = @_; $self->{$k} = uc $v }
}
tie my %loud, 'LoudHash';
$loud{greeting} = 'hi';
say $loud{greeting}; # HI
Μόνο το STORE αλλάζει· τα FETCH, EXISTS, το πρωτόκολλο επανάληψης και ο κατασκευαστής προέρχονται όλα από τη βασική κλάση. Το Tie::Hash (ο γονέας) είναι η αφηρημένη εκδοχή που παρέχει ένα προεπιλεγμένο TIEHASH και new αλλά αφήνει σε εσάς να γράψετε τις μεθόδους προσπέλασης· το Tie::StdHash είναι η συγκεκριμένη που συνήθως θέλετε.
Δείτε επίσης#
tie- η ενσωματωμένη και το πλήρες μενού μεθόδων hashtied- προσεγγίστε το αντικείμενο που στηρίζει για μεθόδους ειδικές της κλάσηςeach- ο επαναλήπτης που υλοποιούν ταFIRSTKEY/NEXTKEY· η δική του σελίδα τεκμηριώνει το συμβόλαιο τέλους επανάληψηςexistsκαιdelete- οι ενσωματωμένες πίσω από ταEXISTSκαιDELETEΒαθμωτά με tie - η απλούστερη διεπαφή πάνω στην οποία βασίζεται αυτή
Tie::Hash- το άρθρωμα της βασικής κλάσης· τοTie::StdHashείναι η συγκεκριμένη εκδοχή