# מערכים קשורים למערך קשור יש שתי שכבות של ממשק. השכבה התחתונה - `FETCH`, `STORE`, `FETCHSIZE`, `STORESIZE`, `CLEAR` - היא חובה ומטפלת בגישה לאלמנטים ובשינוי גודל. השכבה העליונה - `PUSH`, `POP`, `SHIFT`, `UNSHIFT`, `SPLICE`, `DELETE`, `EXISTS` - היא אופציונלית ומאפשרת למערך להגיב לפונקציות המובנות התואמות; ללא הוו נתון, אותה פונקציה מובנית או נופלת חזרה לשכבה התחתונה או מתה. התפריט המלא נמצא בעמוד הייחוס של [`tie`](../../p5/core/perlfunc/tie.md). פרק זה בונה מחלקה, ולאחר מכן מסביר את פרוטוקול הגודל ואת המַשַׁנִּים האופציונליים, כאשר תזמון הקריאה שלהם אומת מול המפרש הרץ. ## מחלקה שלמה מערך מספרי שבו חריצים ריקים נקראים בחזרה כ־`0` במקום `undef`, ושבו `STORESIZE` ממלא חריצים חדשים ב־`0`: ```perl 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`](../../p5/core/perlfunc/push.md), [`pop`](../../p5/core/perlfunc/pop.md), [`shift`](../../p5/core/perlfunc/shift.md), [`unshift`](../../p5/core/perlfunc/unshift.md), [`splice`](../../p5/core/perlfunc/splice.md), [`delete`](../../p5/core/perlfunc/delete.md), ו־[`exists`](../../p5/core/perlfunc/exists.md) מנתבת אל הוו האותיות הגדולות בעל אותו השם כאשר המחלקה מגדירה אותו. תזמון הקריאה שלהן, שאומת מול המפרש: - `push @a, 1, 2` → קריאת `PUSH` אחת עם כל הרשימה. - `pop @a` → `POP`; `shift @a` → `SHIFT`; `unshift @a, 0` → `UNSHIFT`. - `splice @a, 1, 1, 'x', 'y'` → קריאת `SPLICE` אחת הנושאת היסט, אורך, ואת רשימת ההחלפה; ערך ההחזרה שלה הופך לזה של `splice`. - `exists $a[0]` → `EXISTS`; `delete $a[0]` → `DELETE`. כאשר הוו מַשַׁנֶּה **נעדר**, ההתנהגות תלויה באופרטור. `Tie::Array` (להלן) מספק נפילות חזרה לחמשת הנפוצים במונחי השכבה החובה; ללא מחלקת בסיס זו, קריאה למַשַׁנֶּה שאינו ממומש מתה בנקודת השימוש. ## תזמון הקריאה - `$a[$i]` → `FETCH`; `$a[$i] = $v` → `STORE`. - `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` מקבלת את כל מַשַׁנֵּי הרשימה בחינם: ```perl 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`](../../p5/core/perlfunc/delete.md) ול־[`exists`](../../p5/core/perlfunc/exists.md). ## ראו גם - [`tie`](../../p5/core/perlfunc/tie.md) - הפונקציה המובנית ותפריט מתודות המערך המלא - [`tied`](../../p5/core/perlfunc/tied.md) - הגעה אל העצם המגבה - [`splice`](../../p5/core/perlfunc/splice.md) - המַשַׁנֶּה המורכב ביותר שהוו `SPLICE` מייצג - [`push`](../../p5/core/perlfunc/push.md) ו־[`pop`](../../p5/core/perlfunc/pop.md) - המַשַׁנִּים ש־`Tie::Array` גוזר מהשכבה החובה - [האשים קשורים](hashes.md) - ממשק המכל העשיר האחר - `Tie::Array` - מודול מחלקת הבסיס המספק את מַשַׁנֵּי הרשימה