מאפיינים#

מאפיין תת־שגרה הוא הערה בעלת קידומת נקודתיים בין שם תת־השגרה (או החתימה) לבין גופה. מאפיינים מסמנים שלתת־שגרה יש תכונה מיוחדת כלשהי - היותה מתודה, היותה lvalue, היותה בעלת prototype, היותה קבועה, היותה כפופה למנגנון צד שלישי כלשהו - שזמן־הריצה, המנתח, או מטפל מאפיינים שנכתב על־ידי המשתמש יכולים לפעול עליה.

sub greet :method ($name)            { ... }
sub editable :lvalue                 { ... }
sub mypush :prototype(\@@)           { ... }
sub PI :const                        { 3.14159 }
sub handler :Memoize                 { ... }   # third-party attribute

ארבעת שלבי המאפיין#

כדי לעקוב כיצד מאפיין משפיע על תת־שגרה, הבחן בארבעה שלבים:

  1. ניתוח. המהדר קורא :foo(args) ומפרק אותו לשם (foo) ולמחרוזת פרמטרים אופציונלית (args).

  2. רישום. המהדר מפעיל או מטפל מובנה (עבור :lvalue, :method, :prototype(...)) או את MODIFY_CODE_ATTRIBUTES מהחבילה הרלוונטית (עבור מאפיינים שהוגדרו על־ידי המשתמש).

  3. אחסון. מאפיינים מובנים מציבים דגל על ה־CV (עצם תת־השגרה המהודר). מאפייני משתמש מאוחסנים בכל מקום שהמטפל בוחר.

  4. שאילתה. כל אחד יכול מאוחר יותר לבקש מ־attributes::get(\&sub) את רשימת המאפיינים. מאפיינים מובנים ניתנים לשאילתה גם דרך ממשקי API ישירים יותר (prototype, is_lvalue, …).

ארבעת השלבים הם הסיבה שמאפיינים הטרוגניים בהתנהגותם: מאפיינים מובנים הם מושגי זמן־ריצה; מאפייני משתמש הם כל מה שהקוד ב־MODIFY_CODE_ATTRIBUTES מחליט לעשות איתם.

מאפיינים מובנים#

:lvalue#

מסמן את תת־השגרה כשמישה בצד שמאל של השמה. הביטוי האחרון של הגוף חייב להיות lvalue. ראה lvalue והקשר.

sub editable :lvalue { $self->{x} }

$obj->editable = 42;             # writes through to $self->{x}

:method#

רמז למנתח ולהסתכלות־הפנימית שתת־שגרה זו נועדה להיקרא כמתודה. הדגל הוא בעיקרו מידעי; הוא גם מדכא את השפעות ה־prototype (מכיוון שמתודות אינן מכבדות prototypes ממילא):

package Counter;

sub increment :method {
    my $self = shift;
    $self->{n}++;
}

:method אינו מבצע use parent אוטומטית או מציב @ISA; הוא אינו משנה את השיגור. הוא סמן תיעוד / כוונה שזמן־הריצה גם משתמש בו כדי לדלג על עיבוד ה־prototype.

:prototype(...)#

צורת המאפיין של prototype, נדרשת כשחתימות מופעלות באותו תחום:

use feature 'signatures';
sub mygrep :prototype(&@) ($code, @list) {
    grep { $code->($_) } @list;
}

רשימת הפרמטרים של הגוף היא החתימה; המאפיין נושא את ה־prototype. ללא צורת המאפיין, המנתח אינו יכול להבחין איזו רשימה בסוגריים היא איזו.

:const#

מבטיח לזמן־הריצה שגוף תת־השגרה מפיק תוצאה קבועה, ומאפשר שילוב־פנימה אגרסיבי יותר ממה שה־prototype החשוף () נותן:

sub PI :const { 3.14159265358979 }

:const הוצג לשימוש על־ידי use constant וכלים דומים. רוב הקוד צריך לפנות אל use constant במקום ליישם :const ישירות.

מאפיינים של צד שלישי#

כל חבילה יכולה לרשום מטפלי מאפיינים עבור הפניות לקוד באמצעות מימוש MODIFY_CODE_ATTRIBUTES. הדוגמה הקלאסית היא Attribute::Handlers, המספקת ממשק API דקלרטיבי יותר מעל המנגנון הגולמי. מאפיין משתמש נראה זהה למאפיין מובנה באתר הקריאה:

use Some::Memoize;               # provides ':Memoize' attribute

sub expensive :Memoize {
    ...
}

החבילה המספקת את המאפיין מחליטה מה :Memoize אומר. מאפייני צד שלישי נפוצים:

  • :Memoize - מטמן אוטומטית ערכי החזרה (מ־Memoize).

  • :Logged - עוטף את תת־השגרה ברישום כניסה/יציאה ביומן (ממודולים שונים בסגנון AOP).

  • :Test, :Benchmark - סמני מסגרת־בדיקות.

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

כיצד מאפיינים נכתבים#

ניתן לשרשר מספר מאפיינים. הסדר על הדיסק הוא הסדר שבו הם מעובדים:

sub fancy :method :prototype(\@) :Memoize {
    ...
}

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

sub greet :method;               # forward declaration with :method
sub greet :method ($name) {      # body, same attribute
    ...
}

חתימה, כשהיא קיימת, באה לאחר המאפיינים:

sub greet :method ($name) {      # OK
    ...
}

sub greet ($name) :method {      # error: attributes must precede signature
    ...
}

מה מאפיינים אינם עושים#

  • הם אינם הערות טיפוס. :Int אינו מובנה; ניסיונות לקרוא אותו כך לא יעשו דבר בשקט אלא אם חבילה כלשהי רשמה מטפל שמשמעו אותו דבר.

  • הם אינם מטא־נתונים עבור מסגרת הבדיקות אלא אם מסגרת הבדיקות רשמה עבורם מטפל. פיזור :tested על תת־שגרות אינו עושה דבר ללא קוד בצד השני הקורא את ההערה.

  • הם אינם הוראות אופטימיזציה. :fast אינו עושה דבר.

שאילתת המאפיינים של תת־שגרה#

use attributes;

sub demo :method :prototype($) :Memoize { ... }

my @attrs = attributes::get(\&demo);
# returns: ('method', 'prototype($)', 'Memoize')

זהו המקבילה ההסתכלות־פנימית של prototype - הוא עונה ”אילו הערות תת־שגרה זו נושאת?“ ולא ”כיצד תת־שגרה זו נראית למנתח?“.

ראו גם#

  • הצהרה - היכן תחביר ה־:attr משתלב בדקדוק ההצהרה.

  • Prototypes - :prototype(...) הוא מאפיין אחד מתוך כמה.

  • Lvalue והקשר - משמעות :lvalue.

  • prototype - הסתכלות־פנימית בזמן־ריצה על מאפיין מובנה ספציפי אחד.

  • תכנות מונחה־עצמים - :method וההקשר הרחב יותר של תכנות מונחה־עצמים.