האשים

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});
}

מקרי קצה#

  • Autovivification של רמות ביניים. זוהי המלכודת החשובה ביותר היחידה. exists $ref->{A}{B}{C} בודק את המפתח הפנימי ביותר, אך הערכת הביטוי מבצעת autovivify לכל האש ביניים - $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
    

    האלמנט הפנימי ביותר הוא היחיד שאינו מבצע autovivify. אותו דבר קורה דרך הפניות: exists $ref->{A} על $ref שהוא undef מחיה את $ref להפניית האש. אם נדרש לבחון מבנה עמוק מבלי לשנותו, יש לעבור עליו רמה אחת בכל פעם עם exists בכל שלב, או להשתמש בעוזר ייעודי כמו סמנטיקת [Data::Diver] (תחום פרגמה eval / no autovivification משורשר).

  • exists על מפתח עם ערך undef הוא אמת. זהו ההבדל המגדיר מ־defined. delete מסיר גם את המפתח וגם את הערך; השמת undef שומרת את המפתח.

  • גבולות מערך. exists $a[$i] הוא שקר כאשר $i מעבר לסוף המערך או כאשר החריץ deleteed באמצע המערך. אינדקסים שליליים נספרים מהסוף: 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 כושל אינו מבטיח לכן שקריאה הייתה נכשלת.

  • משתנים קשורים (tied). עבור האשים ומערכים קשורים, 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 הוא כבר רק־סקלרי ואינו זקוק לו