מערכים קשורים#

למערך קשור יש שתי שכבות של ממשק. השכבה התחתונה - FETCH, STORE, FETCHSIZE, STORESIZE, CLEAR - היא חובה ומטפלת בגישה לאלמנטים ובשינוי גודל. השכבה העליונה - PUSH, POP, SHIFT, UNSHIFT, SPLICE, DELETE, EXISTS - היא אופציונלית ומאפשרת למערך להגיב לפונקציות המובנות התואמות; ללא הוו נתון, אותה פונקציה מובנית או נופלת חזרה לשכבה התחתונה או מתה.

התפריט המלא נמצא בעמוד הייחוס של tie. פרק זה בונה מחלקה, ולאחר מכן מסביר את פרוטוקול הגודל ואת המַשַׁנִּים האופציונליים, כאשר תזמון הקריאה שלהם אומת מול המפרש הרץ.

מחלקה שלמה#

מערך מספרי שבו חריצים ריקים נקראים בחזרה כ־0 במקום undef, ושבו STORESIZE ממלא חריצים חדשים ב־0:

use v5.36;

package ZeroFill {
    sub TIEARRAY  { my $class = shift; return bless { data => [] }, $class }
    sub FETCH     { my ($self, $i) = @_; return $self->{data}[$i] // 0 }
    sub STORE     { my ($self, $i, $v) = @_; $self->{data}[$i] = $v }
    sub FETCHSIZE { my $self = shift; return scalar @{ $self->{data} } }
    sub STORESIZE {
        my ($self, $n) = @_;
        my $data = $self->{data};
        if ($n > @$data) {
            push @$data, (0) x ($n - @$data);   # pad with 0, not undef
        } else {
            $#$data = $n - 1;
        }
    }
    sub CLEAR { my $self = shift; $self->{data} = [] }
    sub PUSH  { my $self = shift; push @{ $self->{data} }, @_; return scalar @{ $self->{data} } }
    sub POP   { my $self = shift; return pop @{ $self->{data} } }
}

tie my @a, 'ZeroFill';
$a[0] = 11;
$a[2] = 33;                # FETCH on the skipped slot 1 reads back 0
say scalar @a;             # 3
say $a[1];                 # 0
$#a = 4;                   # STORESIZE grows the array, padding with 0
say "@a";                  # 11 0 33 0 0
push @a, 44;
say pop @a;                # 44

העצם הוא הפניה blessed להאש העוטפת מערך אמיתי בחריץ ה־data שלה - תבנית נפוצה, מכיוון שהמחלקה בדרך כלל רוצה מקום למצב מעבר למערך עצמו.

השכבה החובה#

TIEARRAY classname, LIST

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

FETCH this, index / STORE this, index, value

קריאה וכתיבה של אלמנט יחיד. אינדקס שלילי מתורגם לחיובי - זמן הריצה קורא תחילה ל־FETCHSIZE ומוסיף אותו לאינדקס - אלא אם המחלקה מציבה ב־$NEGATIVE_INDICES ערך אמת, ובמקרה זה האינדקס השלילי הגולמי מגיע אל FETCH ואל STORE.

FETCHSIZE this

מחזיר את מספר האלמנטים. זה עומד מאחורי scalar @array ומאחורי סימן הגובה העליון של $#array. זו המתודה הנקראת ביותר על מערך קשור, מכיוון שזמן הריצה זקוק לגודל כדי לנרמל אינדקסים שליליים וכדי לתחום את האיטרציה.

STORESIZE this, count

מציב את אורך המערך. הגדלה צריכה למלא את החריצים החדשים במושג ה״ריק״ של המחלקה (כאן, 0); הקטנה צריכה להשמיט את העודף. זה עומד מאחורי $#array = N ומאחורי שינוי הגודל המונע על ידי השמה.

CLEAR this

מרוקן את המערך - מופעל על ידי @array = ().

פרוטוקול הגודל בפועל#

FETCHSIZE ו־STORESIZE הם המקום שבו מערכים קשורים נבדלים יותר מכל דבר אחר מהאשים קשורים. שלושה דברים שכדאי לזכור:

  • $#a קורא דרך FETCHSIZE וכותב דרך STORESIZE. הצבת $#a = 4 קוראת ל־STORESIZE($obj, 5).

  • חריצים שדולגו הם הבעיה של המחלקה. השמה ל־$a[2] כאשר אורך המערך הוא 1 קוראת ל־STORE($obj, 2, ...) ישירות - זמן הריצה אינו קורא ל־STORESIZE כדי למלא את הפער. אם קריאה של חריץ 1 צריכה להחזיר 0 ולא undef, על FETCH לומר זאת (ה־// 0 בדוגמה).

  • EXTEND הוא מייעץ. כאשר זמן הריצה מצפה שמערך יגדל

    • למשל ממש לפני השמה גדולה - הוא עשוי לקרוא ל־EXTEND this, count כרמז להקצאה מוקדמת. תמיד בטוח להשאיר את EXTEND בלתי־מוגדר או ריק; זוהי הזדמנות אופטימיזציה גרידא, וגרימה לו להתנהג כמו STORESIZE היא באג, מכיוון שזמן הריצה קורא ל־EXTEND בזמנים שבהם הוא למעשה אינו רוצה שהאורך הנראה ישתנה.

המַשַׁנִּים האופציונליים#

כל אחת מ־push, pop, shift, unshift, splice, delete, ו־exists מנתבת אל הוו האותיות הגדולות בעל אותו השם כאשר המחלקה מגדירה אותו. תזמון הקריאה שלהן, שאומת מול המפרש:

  • push @a, 1, 2 → קריאת PUSH אחת עם כל הרשימה.

  • pop @aPOP; shift @aSHIFT; unshift @a, 0UNSHIFT.

  • splice @a, 1, 1, 'x', 'y' → קריאת SPLICE אחת הנושאת היסט, אורך, ואת רשימת ההחלפה; ערך ההחזרה שלה הופך לזה של splice.

  • exists $a[0]EXISTS; delete $a[0]DELETE.

כאשר הוו מַשַׁנֶּה נעדר, ההתנהגות תלויה באופרטור. Tie::Array (להלן) מספק נפילות חזרה לחמשת הנפוצים במונחי השכבה החובה; ללא מחלקת בסיס זו, קריאה למַשַׁנֶּה שאינו ממומש מתה בנקודת השימוש.

תזמון הקריאה#

  • $a[$i]FETCH; $a[$i] = $vSTORE.

  • scalar @a, נרמול אינדקס שלילי → FETCHSIZE.

  • $#a = $n, שינוי גודל של @a = (...)STORESIZE (ו־CLEAR תחילה עבור השמה של המערך כולו).

  • push/pop/shift/unshift/splice/delete/exists → הוו האותיות הגדולות התואם כאשר הוא מוגדר.

  • פרוסת מערך @a[1, 3] קוראת ל־FETCH פעם אחת לכל אינדקס; אין הוו לפרוסה.

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

המערך הקשור הקטן ביותר מגדיר TIEARRAY, FETCH, STORE, FETCHSIZE, ו־STORESIZE. עם חמשת אלה, גישה לאלמנטים, scalar @a, $#a, והאיטרציה כולם עובדים. הוסיפו CLEAR כדי ש־@a = () יתנהג כראוי. הוסיפו את המַשַׁנִּים רק עבור הפונקציות המובנות שהקוראים שלכם משתמשים בהן בפועל - או ירשו אותם.

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

Tie::Array ממש את PUSH, POP, SHIFT, UNSHIFT, ו־SPLICE במונחי החמישה החובה, כך שתת־מחלקה המספקת TIEARRAY, FETCH, STORE, FETCHSIZE, ו־STORESIZE מקבלת את כל מַשַׁנֵּי הרשימה בחינם:

package Logging {
    use Tie::Array;
    our @ISA = ('Tie::Array');
    sub TIEARRAY  { my $class = shift; return bless { data => [] }, $class }
    sub FETCH     { my ($self, $i) = @_; return $self->{data}[$i] }
    sub STORE     { my ($self, $i, $v) = @_; warn "store $i\n"; $self->{data}[$i] = $v }
    sub FETCHSIZE { my $self = shift; return scalar @{ $self->{data} } }
    sub STORESIZE { my ($self, $n) = @_; $#{ $self->{data} } = $n - 1 }
}

tie my @log, 'Logging';
push @log, 'x', 'y';        # PUSH inherited; calls STORE twice (warns)
say "@log";                # x y

push @log, 'x', 'y' עובד ללא מתודת PUSH מכיוון שהמתודה הנירשת מתרחבת לשתי קריאות STORE - ולכן הדוגמה מתריעה פעמיים. Tie::Array אינו מגדיר את TIEARRAY בעצמו, ולכן תת־המחלקה חייבת לספק את הבנאי; ה־DELETE וה־EXISTS המוגדרים כברירת מחדל פשוט עושים croak, אז דרסו אותם אם הקוראים שלכם זקוקים ל־delete ול־exists.

ראו גם#

  • tie - הפונקציה המובנית ותפריט מתודות המערך המלא

  • tied - הגעה אל העצם המגבה

  • splice - המַשַׁנֶּה המורכב ביותר שהוו SPLICE מייצג

  • push ו־pop - המַשַׁנִּים ש־Tie::Array גוזר מהשכבה החובה

  • האשים קשורים - ממשק המכל העשיר האחר

  • Tie::Array - מודול מחלקת הבסיס המספק את מַשַׁנֵּי הרשימה