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} = $vSTORE.

  • exists $h{k}EXISTS· delete $h{k}DELETE.

  • %h = ()CLEAR· %h = (a => 1)CLEAR και μετά STORE.

  • keys %h / values %h / each %hFIRSTKEY μία φορά, και μετά 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 - η ενσωματωμένη και το πλήρες μενού μεθόδων hash

  • tied - προσεγγίστε το αντικείμενο που στηρίζει για μεθόδους ειδικές της κλάσης

  • each - ο επαναλήπτης που υλοποιούν τα FIRSTKEY / NEXTKEY· η δική του σελίδα τεκμηριώνει το συμβόλαιο τέλους επανάληψης

  • exists και delete - οι ενσωματωμένες πίσω από τα EXISTS και DELETE

  • Βαθμωτά με tie - η απλούστερη διεπαφή πάνω στην οποία βασίζεται αυτή

  • Tie::Hash - το άρθρωμα της βασικής κλάσης· το Tie::StdHash είναι η συγκεκριμένη εκδοχή