Hashes#
Ένα hash είναι μια μη διατεταγμένη συλλογή ζευγών κλειδιού-τιμής όπου τα κλειδιά είναι συμβολοσειρές και οι τιμές είναι βαθμωτά. Το sigil είναι % για ολόκληρο το hash, @ για τομές τιμών και τομές κλειδιού/τιμής, και $ για μία τιμή:
my %user = (
name => 'John',
age => 30,
email => 'john@example.com',
);
%user # the whole hash (six elements: key1, val1, ...)
$user{name} # one value - 'John'
@user{qw(name age)} # value slice - ('John', 30)
%user{qw(name age)} # key/value slice - (name => 'John', age => 30)
keys %user # the list of keys
Τα κλειδιά hash εξαναγκάζονται πάντα σε συμβολοσειρές. Η αποθήκευση με ακέραιο κλειδί 42 και η ανάγνωση με κλειδί συμβολοσειράς "42" ανακτούν την ίδια θέση - είναι το ίδιο κλειδί.
Αρχικοποίηση: ζεύγη, fat comma και %h = LIST#
Ένα hash αρχικοποιείται από μια λίστα ζυγού μήκους, σε σειρά κλειδιού/τιμής:
my %h = ('a', 1, 'b', 2); # legal but ugly
my %h = (a => 1, b => 2); # idiomatic
Το fat comma => είναι ένα κόμμα που επίσης κάνει αυτόματα εισαγωγικά σε ένα bareword στα αριστερά που μοιάζει με αναγνωριστικό. Έτσι το a => 1 είναι ακριβώς 'a', 1. Η αυτόματη εισαγωγή σε εισαγωγικά απαιτεί το bareword να είναι απλό αναγνωριστικό - το 2.0 => 'x' αναλύεται ως ο αριθμός 2, όχι η συμβολοσειρά "2.0":
my %h = (a => 1); # ('a', 1) - auto-quoted
my %h = ('a' => 1); # ('a', 1) - same
my %h = (2.0 => 'x'); # (2, 'x') - not ('2.0', 'x')!
my %h = ("2.0" => 'x'); # ('2.0', 'x') - explicit quote
Αν ένα κλειδί εμφανίζεται περισσότερες από μία φορές στη λίστα αρχικοποίησης, κερδίζει η τελευταία εμφάνιση. Το τυπικό ιδίωμα για «συγχώνευση με υπερισχύσεις» εκμεταλλεύεται αυτό:
my %config = (%defaults, %overrides);
# %config has every default key, with %overrides values where they collide
Πρόσβαση, με τις τέσσερις μορφές sigil#
Η ανάγνωση και εγγραφή μίας θέσης γίνεται με sigil $:
my $name = $user{name}; # read
$user{city} = 'NYC'; # write
$user{age}++; # arithmetic on a hash value
delete $user{email}; # remove the slot entirely
Οι άλλες τρεις μορφές subscript αντιστοιχούν σε αυτό που θέλετε πίσω:
@user{qw(name age city)} # ('John', 31, 'NYC') - values
%user{qw(name age)} # (name => 'John', age => 31) - pairs
keys %user # ('name', 'age', 'city') - keys
values %user # ('John', 31, 'NYC') - values
Δείτε subscript για τον πλήρη πίνακα δώδεκα μορφών και τον κανόνα αυτόματης εισαγωγής bareword σε εισαγωγικά μέσα σε {}.
exists έναντι defined έναντι αλήθειας#
Τρεις διακριτές ερωτήσεις για ένα κλειδί:
exists $h{key} # is the slot present at all?
defined $h{key} # is the slot present AND non-undef?
$h{key} # is the value true (non-empty, non-zero, non-"0")?
Αυτές διαφέρουν όταν:
$h{a} = undef;
$h{b} = 0;
delete $h{c};
exists $h{a} # TRUE - slot exists, value is undef
defined $h{a} # FALSE - value is undef
$h{a} # FALSE - undef is false
exists $h{b} # TRUE
defined $h{b} # TRUE
$h{b} # FALSE - 0 is false
exists $h{c} # FALSE - never set, or deleted
defined $h{c} # FALSE
$h{c} # FALSE
Επιλέξτε κατά πρόθεση: exists για παρουσία, defined για τιμή-όχι-undef, αλήθεια για ουσιαστική τιμή. Η χρήση if ($h{key}) όταν εννοείτε exists είναι πηγή σφαλμάτων - τα 0 και "" είναι έγκυρες τιμές που ο έλεγχος απορρίπτει.
Επανάληψη: keys, values, each, while (each)#
for my $k (keys %user) {
print "$k = $user{$k}\n";
}
for my $v (values %user) { ... } # values only
while (my ($k, $v) = each %user) { # keys + values, one pair per call
print "$k = $v\n";
}
Η each μεταφέρει κατάσταση επαναλήπτη πάνω στο ίδιο το hash· η κλήση keys (ή values) στο hash επαναφέρει αυτόν τον επαναλήπτη. Ο συνδυασμός των δύο παράγει σφάλματα βρόχου δύσκολα στην αποσφαλμάτωση:
while (my ($k, $v) = each %h) {
if (some_condition($k)) {
print "size: ", scalar keys %h, "\n"; # resets each() iterator!
# next iteration of while() starts over from the top
}
}
Διόρθωση: είτε συσσωρεύστε τη λίστα με keys μία φορά στην αρχή, είτε αποφύγετε εντελώς την each. Η περισσότερη επανάληψη σε hash γράφεται καλύτερα με keys:
for my $k (sort keys %h) { # bonus: defined ordering
...
}
Σειρά κλειδιών#
Τα κλειδιά hash εξάγονται σε σειρά σειράς-εισαγωγής-διαταραγμένης-από-τυχαιοποίηση-hash - δηλαδή δεν υπάρχει εγγυημένη σειρά. Δύο εκτελέσεις του ίδιου προγράμματος με την ίδια είσοδο μπορεί να επαναλαμβάνουν τα κλειδιά σε διαφορετική σειρά. Αυτό είναι χαρακτηριστικό ασφαλείας (αποτρέπει επιθέσεις αλγοριθμικής πολυπλοκότητας εναντίον της συνάρτησης κατακερματισμού)· είναι επίσης μια επαναλαμβανόμενη πηγή εύθραυστων ελέγχων:
my %h = (a => 1, b => 2, c => 3);
print "$_ " for keys %h; # output order is undefined
Αν χρειάζεστε σταθερή σειρά, ταξινομήστε:
print "$_ " for sort keys %h; # alphabetical
print "$_ " for sort { $h{$a} <=> $h{$b} } keys %h; # by value
Αναφορές hash#
Ένα hash, όπως και ένας πίνακας, ισοπεδώνεται όταν περνάει μέσω λίστας. Για να μεταβιβάσετε ένα hash χωρίς ισοπέδωση, πάρτε μια αναφορά:
my %h = (a => 1, b => 2);
my $href = \%h; # reference to the existing %h
my $anon = { x => 1, y => 2 }; # anonymous hash reference
$href->{a} # access through the arrow - 1
${$href}{a} # fully bracketed deref - 1
keys %$href # deref then keys
%{$href} # deref to flat key/value list
Δείτε references για την πλήρη εικόνα.
Πραγματικό παράδειγμα: μέτρηση και ομαδοποίηση#
Ο μετρητής συχνότητας είναι το σχολικό μικρό παράδειγμα για hashes:
my @words = qw(apple banana apple cherry apple banana);
my %count;
$count{$_}++ for @words;
# %count = (apple => 3, banana => 2, cherry => 1)
Η ομαδοποίηση είναι το επόμενο βήμα - κατασκευή hash του οποίου οι τιμές είναι αναφορές πίνακα:
my @people = (
{ name => 'Alice', dept => 'eng' },
{ name => 'Bob', dept => 'sales' },
{ name => 'Carol', dept => 'eng' },
{ name => 'Dan', dept => 'sales' },
);
my %by_dept;
push @{ $by_dept{$_->{dept}} }, $_->{name} for @people;
# %by_dept = (eng => ['Alice', 'Carol'], sales => ['Bob', 'Dan'])
Η γραμμή push @{ $by_dept{$_->{dept}} }, ... αυτο-δημιουργεί: όταν το κλειδί eng δεν υπάρχει ακόμα, δημιουργείται η θέση στο hash και εισάγεται ένας ανώνυμος πίνακας, στη συνέχεια η τιμή γίνεται push. Δείτε references για την ιστορία της αυτο-δημιουργίας.
Τομή hash ως πολλαπλή ανάκτηση/ανάθεση#
my %config = (host => 'localhost', port => 80, debug => 0);
my ($h, $p) = @config{'host', 'port'}; # ('localhost', 80)
@config{qw(host port debug)} = ('elsewhere', 8080, 1); # set three at once
Συνδυάστε με τομή %h{...} για «εξαγωγή εγγραφής»:
my %fields = %config{qw(host port)};
# %fields = (host => 'localhost', port => 80)
Αυτός είναι ο κανονικός τρόπος να προβάλλετε ένα hash σε υποσύνολο των κλειδιών του. Χωρίς %h{...} θα έπρεπε να το φτιάξετε χειροκίνητα με βρόχο ή με map.
Δείτε επίσης#
Arrays - όταν ο δείκτης είναι ακέραιος.
References - αυτο-δημιουργία, hash-of-arrays, hash-of-hashes· η μορφή
$h->{a}{b}.Subscript and slice operators - ο κανόνας αυτόματης εισαγωγής bareword σε εισαγωγικά και οι τέσσερις μορφές τομής.
exists,defined,delete- η τριάδα παρουσίας/τιμής/αφαίρεσης.sort- για ταξινομημένη επανάληψη.tie- δημιουργία hash υποστηριζόμενου από κάτι άλλο εκτός από τον πίνακα κατακερματισμού στη μνήμη (αρχείο DBM, νωχελική βάση δεδομένων, …).