האשים קשורים#

האש קשור הוא העשיר ביותר מבין סוגי ה־tie, מכיוון שהאש עושה יותר מאשר קריאה וכתיבה של אלמנטים בודדים - הוא מבצע איטרציה, מדווח על גודלו, בודק קיום של מפתח, ומתרוקן. כל אחד מאלה הוא הוו נפרד. האשים היו סוג הנתונים הראשון ב־Perl שנקשר (מקרה השימוש המקורי היה קישור של %hash לקובץ DBM על הדיסק), והממשק עדיין משקף מורשת זו.

תפריט המתודות המלא נמצא בעמוד הייחוס של tie. פרק זה בונה מחלקה אחת ומסביר כל הוו, עם תשומת לב לחלק שמכשיל אנשים: פרוטוקול האיטרציה.

מחלקה שלמה#

האש שמפתחותיו אינם רגישים לאותיות גדולות/קטנות - $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 להאש המשמשת כאחסון האמיתי; ההוּוים ממירים את המפתח לאותיות קטנות לפני שהם נוגעים בו. שימו לב שהאיטרציה מחזירה את המפתחות המאוחסנים (באותיות קטנות), וזה בדיוק מה שהאש שאינו רגיש לאותיות גדולות/קטנות צריך לעשות.

חוזה המתודות#

TIEHASH classname, LIST

הבנאי. מחזיר הפניה blessed - בדרך כלל אך לא בהכרח הפניה להאש - שהופכת לעצם הקשור.

FETCH this, key / STORE this, key, value

קריאה וכתיבה של אלמנט יחיד, אותו זוג כמו עבור סקלרים אך עם ארגומנט מפתח. ערך ההחזרה של STORE מתעלם.

EXISTS this, key / DELETE this, key

עומדים מאחורי הפונקציות המובנות exists ו־delete. ערך ההחזרה של DELETE הופך לערך ההחזרה של delete; כדי להתאים להאש רגיל, החזירו את מה ש־FETCH היה מחזיר עבור אותו מפתח לפני הסרתו.

CLEAR this

מופעל כאשר ההאש כולו מרוקן, בדרך כלל על ידי השמת הרשימה הריקה (%h = ()). הסירו הכול.

FIRSTKEY this / NEXTKEY this, lastkey

פרוטוקול האיטרציה - ראו להלן. יחד הם מניעים את keys, values, ו־each.

SCALAR this

נקרא כאשר ההאש מוערך בהקשר סקלרי או בוליאני (scalar %h, if (%h), ומאז 5.28 גם keys %h בהקשר בוליאני). החזירו ערך שהוא אמת כאשר ההאש אינו ריק ושקר כאשר הוא ריק.

DESTROY this ו־UNTIE this

הוּוי ניקוי אופציונליים, זהים בתפקידם למקרה הסקלרי. UNTIE מכוסה תחת מלכודת ה־untie להלן.

פרוטוקול האיטרציה#

זהו החלק של ממשק ההאש שאין לו מקבילה בממשק הסקלרי, והחלק שהכי הרבה פעמים משתבש.

כאשר איטרציה של keys, values, או each מתחילה, זמן הריצה קורא ל־FIRSTKEY כדי לקבל את המפתח הראשון, ולאחר מכן ל־NEXTKEY שוב ושוב, מעביר בכל פעם את המפתח שהוחזר קודם, עד שמתודה מחזירה את הרשימה הריקה או undef. שניהם נקראים תמיד בהקשר סקלרי וצריכים להחזיר מפתח בלבד; זמן הריצה קורא ל־FETCH בעצמו כדי לקבל כל ערך.

שני כללים שומרים על נכונות האיטרציה:

  • FIRSTKEY חייב לאפס כל איטרטור פנימי. כאשר האחסון המגבה הוא האש אמיתי, העריכו תחילה את keys %$self בהקשר ריק או סקלרי - זה מאפס את סמן ה־each של Perl לכל האש - ולאחר מכן קראו ל־each. ה־my $reset = keys %$self החד־פעמי בדוגמה עושה בדיוק זאת. דלגו עליו ו־keys %h חדש עלול להמשיך מהמקום שבו האיטרציה האחרונה נעצרה.

  • אותתו על הסוף באמצעות הרשימה הריקה. כאשר האחסון המגבה אינו האש אמיתי ואין לו each משלו, החזירו את הרשימה הריקה (או undef) ברגע שמסרתם את המפתח האחרון.

מדוע כדאי להגדיר את SCALAR#

ללא מתודת SCALAR, זמן הריצה מנחש את ערך האמת של ההאש בהקשר בוליאני, והניחוש עלול להיות שגוי - בפרט, הוא עשוי לדווח על האש כלא ריק מיד לאחר שרוקנתם אותו באמצעות 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 (אם מוגדר).

  • פרוסת האש @h{qw(a b)} קוראת ל־FETCH פעם אחת לכל מפתח; אין הוו לפרוסה.

מחלקה מינימלית בת־קיימא#

האש קשור לקריאה/כתיבה זקוק לכל הפחות ל־TIEHASH, FETCH, ו־STORE. אם הקוראים ירצו אי פעם לבצע עליו איטרציה, הוסיפו FIRSTKEY ו־NEXTKEY - בלעדיהם, keys ו־each רואים האש ריק. הוסיפו EXISTS ו־DELETE אם הקוראים משתמשים בפונקציות מובנות אלה; בלעדיהם, exists/delete מתים בנקודת השימוש. SCALAR ו־CLEAR משלימים את הנכונות עבור בדיקות בוליאניות ורִיקון בכמות.

הדרך הקלה: לרשת מ־Tie::StdHash#

Tie::StdHash (המסופק בתוך Tie::Hash) ממש את התפריט כולו מעל הפניה blessed להאש. ירשו ממנו ודרסו רק את ההוּוים שצריכים להתנהג אחרת:

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 - הפונקציה המובנית ותפריט מתודות ההאש המלא

  • tied - הגעה אל העצם המגבה עבור מתודות ספציפיות למחלקה

  • each - האיטרטור ש־FIRSTKEY / NEXTKEY ממשים; העמוד שלו עצמו מתעד את חוזה סוף־האיטרציה

  • exists ו־delete - הפונקציות המובנות שמאחורי EXISTS ו־DELETE

  • סקלרים קשורים - הממשק הפשוט יותר שזה נבנה עליו

  • Tie::Hash - מודול מחלקת הבסיס; Tie::StdHash היא הגרסה הקונקרטית