Hashes

exists#

Έλεγχος εάν ένα στοιχείο πίνακα κατακερματισμού ή πίνακα, ή μια ονομασμένη υπορουτίνα, είναι παρόν - χωρίς να το δημιουργεί και χωρίς να ενδιαφέρεται για το τι κρατά.

Η exists θέτει ένα δομικό ερώτημα: υπάρχει εγγραφή σε αυτή τη θέση; Δεν ρωτά ποια είναι η τιμή της εγγραφής. Μια εγγραφή της οποίας η τιμή είναι undef εξακολουθεί να υπάρχει. Μια εγγραφή που δεν έχει εκχωρηθεί ποτέ δεν υπάρχει - η αναζήτησή της με απλή ανάγνωση θα επέστρεφε undef, αλλά η exists διακρίνει τις δύο περιπτώσεις.

Σύνοψη#

exists $hash{KEY}
exists $array[INDEX]
exists &subroutine
exists $ref->{A}{B}          # autovivifies $ref->{A}!

Τι επιστρέφεται#

Μια λογική τιμή: 1 (αληθές βαθμωτό) αν το στοιχείο ή η υπορουτίνα είναι παρόν, η κενή συμβολοσειρά "" (ψευδές βαθμωτό) αν όχι. Το αποτέλεσμα είναι πάντα ένα απλό βαθμωτό - δεν υπάρχει παραλλαγή σε περιβάλλον λίστας.

my %h = (a => undef);
exists $h{a};                # 1   (key exists)
defined $h{a};               # ""  (value is undef)
exists $h{b};                # ""  (key does not exist)

Η exists δεν είναι η defined#

Οι δύο απαντούν σε διαφορετικά ερωτήματα και διαφωνούν ακριβώς όταν μια υπάρχουσα εγγραφή κρατά undef:

Κατάσταση

exists

defined

Απλή ανάγνωση

Δεν εκχωρήθηκε ποτέ

ψευδές

ψευδές

undef

Εκχωρημένο σε undef

αληθές

ψευδές

undef

Εκχωρημένο σε οποιαδήποτε άλλη τιμή

αληθές

αληθές

αυτή η τιμή

Χρησιμοποιήστε την exists όταν χρειάζεται να γνωρίζετε αν ένα κλειδί έχει τοποθετηθεί στη δομή - π.χ. έναν πίνακα κατακερματισμού επιλογών όπου ένα σκέτο κλειδί σημαίνει «ο καλών δήλωσε συμμετοχή, η τιμή δεν έχει σημασία». Χρησιμοποιήστε την defined όταν χρειάζεται να γνωρίζετε αν μια τιμή είναι χρησιμοποιήσιμη.

Παραδείγματα#

Έλεγχος κλειδιού πίνακα κατακερματισμού. Κλασικό ιδίωμα πίνακα κατακερματισμού επιλογών - παρουσία, όχι αληθοτιμή:

sub configure {
    my %opt = @_;
    my $verbose = exists $opt{verbose};     # caller passed verbose => anything
    my $color   = $opt{color} // "auto";    # caller-supplied or default
    ...
}

Διάκριση μεταξύ «λείπει» και «παρόν αλλά undef»:

my %cache = (user_42 => undef);             # cached: "we looked, nothing there"
if (exists $cache{user_42}) {
    # hit - even though the value is undef, we already did the lookup
} else {
    # miss - have to query the database
}

Έλεγχος αραιού πίνακα. Η διαγραφή ενός στοιχείου πίνακα αφήνει μια τρύπα που η exists αναφέρει ως ψευδή:

my @a = (10, 20, 30);
delete $a[1];
exists $a[0];                # 1
exists $a[1];                # ""   (hole)
exists $a[2];                # 1
exists $a[99];               # ""   (past the end)

Ύπαρξη υπορουτίνας. Αληθές μόλις η υπορουτίνα δηλωθεί, ακόμη και αν το σώμα είναι μια προδήλωση χωρίς ορισμό ακόμα:

sub later;                   # forward declaration
exists &later;               # 1
defined &later;              # ""   (no body yet)

sub later { 42 }
defined &later;              # 1

Εμφωλευμένη δομή - ο ασφαλής τρόπος. Ελέγξτε κάθε επίπεδο πριν την κάθοδο, επειδή η μορφή με αλυσίδα βελών έχει παρενέργεια (βλέπε Οριακές περιπτώσεις):

if (exists $tree{users}
    && exists $tree{users}{$id}
    && exists $tree{users}{$id}{email}) {
    send_mail($tree{users}{$id}{email});
}

Οριακές περιπτώσεις#

  • Αυτο-δημιουργία ενδιάμεσων επιπέδων. Αυτή είναι η πιο σημαντική παγίδα. Η exists $ref->{A}{B}{C} ελέγχει το πιο εσωτερικό κλειδί, αλλά η αποτίμηση της έκφρασης αυτο-δημιουργεί κάθε ενδιάμεσο πίνακα κατακερματισμού - οι $ref->{A} και $ref->{A}{B} εμφανίζονται ως κενοί πίνακες κατακερματισμού ακόμη και όταν ο έλεγχος exists επιστρέφει ψευδές:

    my %h;
    exists $h{a}{b}{c};          # returns "" (false)
    exists $h{a};                # now returns 1 - $h{a} was created!
    exists $h{a}{b};             # likewise - now an empty hash
    

    Το πιο εσωτερικό στοιχείο είναι το μόνο που δεν αυτο-δημιουργείται. Το ίδιο συμβαίνει μέσω αναφορών: η exists $ref->{A} σε ένα undef $ref αυτο-δημιουργεί την $ref σε αναφορά πίνακα κατακερματισμού. Αν χρειάζεται να εξετάσετε μια βαθιά δομή χωρίς να την μεταλλάσσετε, διασχίστε την ένα επίπεδο τη φορά με exists σε κάθε βήμα, ή χρησιμοποιήστε ειδικό βοηθητικό όπως τη σημασιολογία [Data::Diver] (μια αλυσίδα eval / εμβέλεια pragma no autovivification).

  • Η exists σε κλειδί με τιμή undef είναι αληθής. Αυτή είναι η καθοριστική διαφορά από την defined. Η delete αφαιρεί τόσο το κλειδί όσο και την τιμή· η εκχώρηση undef διατηρεί το κλειδί.

  • Όρια πίνακα. Η exists $a[$i] είναι ψευδής όταν το $i είναι πέραν του τέλους του πίνακα ή όταν η θέση έχει διαγραφεί με delete στη μέση του πίνακα. Οι αρνητικοί δείκτες μετρούν από το τέλος: η exists $a[-1] ελέγχει το τελευταίο στοιχείο αν ο πίνακας είναι μη κενός.

  • Η exists σε πίνακα αποθαρρύνεται upstream. Η έννοια της διαγραφής ενός στοιχείου πίνακα δεν είναι εννοιολογικά καθαρή - η delete $a[1] αφήνει τρύπα αντί να μετατοπίζει. Ο περισσότερος κώδικας που καταφεύγει στην exists $a[$i] εξυπηρετείται καλύτερα από έναν έλεγχο ορίων ($i <= $#a) ή με τη χρήση ενός πίνακα κατακερματισμού.

  • Η exists &sub() είναι συντακτικό σφάλμα. Το όρισμα πρέπει να ονοματίζει μια υπορουτίνα, όχι να καλεί μία. Η exists &sub ρωτά «είναι δηλωμένη η sub;»· η exists &sub() θα σήμαινε «υπάρχει η τιμή επιστροφής της κλήσης sub;», κάτι που είναι άνευ νοήματος.

  • Η AUTOLOAD δεν μετρά. Η exists &sub είναι ψευδής για μια υπορουτίνα που θα δημιουργούνταν μέσω AUTOLOAD στην πρώτη κλήση. Μια αποτυχημένη exists συνεπώς δεν εγγυάται ότι μια κλήση θα αποτύχει.

  • Δεσμευμένες μεταβλητές. Για δεσμευμένους πίνακες κατακερματισμού και πίνακες, η exists καλεί τη μέθοδο EXISTS στο δεσμευμένο αντικείμενο. Η υλοποίηση tie αποφασίζει τι σημαίνει «ύπαρξη» - μια tie DBM ελέγχει την εγγραφή στον δίσκο, μια οκνηρή tie μπορεί να υλοποιήσει την εγγραφή. Δείτε το perltie.

  • Το όρισμα πρέπει να είναι έκφραση lvalue της οποίας η τελική λειτουργία είναι αναζήτηση κλειδιού ή δείκτη, ή όνομα υπορουτίνας. Τα exists func(), exists $scalar, exists @array, exists %hash είναι όλα σφάλματα. Γίνονται δεκτά μόνο η πρόσβαση σε στοιχείο και το &sub.

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

Δείτε επίσης#

  • defined - ρωτά αν η τιμή είναι χρησιμοποιήσιμη, όχι αν το κλειδί είναι παρόν· διαφέρει από την exists μόνο όταν η αποθηκευμένη τιμή είναι undef

  • delete - αφαιρεί μια εγγραφή ώστε η exists σε αυτή να γίνει ψευδής· το αντίστοιχο της exists

  • each - επαναλαμβάνει ζεύγη κλειδιού/τιμής ενός πίνακα κατακερματισμού· επισκέπτεται μόνο κλειδιά για τα οποία η exists είναι αληθής

  • keys - επιστρέφει τη λίστα των υπαρχόντων κλειδιών· η exists $h{$k} ισοδυναμεί με το ερώτημα αν το $k είναι μεταξύ των keys %h, αλλά O(1) αντί για O(n)

  • ref - κατηγοριοποιεί σε τι δείχνει μια αναφορά· χρήσιμη πριν την exists σε $ref->{...} αν δεν είστε σίγουροι ότι η $ref είναι αναφορά πίνακα κατακερματισμού

  • scalar - εξαναγκασμός βαθμωτού περιβάλλοντος· σημειώστε ότι η exists είναι ήδη μόνο βαθμωτή και δεν τη χρειάζεται