תיחום: my, our, local, state#

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

מצהיר

היקף

אורך חיים

שימוש טיפוסי

my

לקסיקלי (הבלוק)

עד שהבלוק מסתיים

ברירת המחדל; מקומיים פרטיים

our

alias לקסיקלי לחבילה

כל עוד החבילה קיימת

גלובלים משותפים של החבילה עם שם קצר

local

דינמי (עץ הקריאות)

עד שהבלוק מסתיים

שנה זמנית משתנה מיוחד

state

לקסיקלי (הבלוק)

אורך חיי התוכנית

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

my - מקומיים לקסיקליים#

my הוא המצהיר היומיומי:

sub greet {
    my ($name) = @_;
    my $message = "Hello, $name!";
    return $message;
}

המשתנים קיימים למשך הקריאה, פרטיים לגוף, ומאוחסנים בpad של תת־השגרה (משבצת אחת לכל לקסיקל מוצהר). הם אינם גלויים לקוראים, לנקראים, או לקוד eval שאינו מקונן טקסטואלית בתוך תת־השגרה.

משתני my הם היחידים שנלכדים ב־closure כראוי. closures לוכדים תאים ב־pad המקיף, ורק משתני my (ו־state) מקבלים תאי pad.

our - גלובלים של החבילה עם alias לקסיקלי#

package Counter;

sub increment {
    our $value;                  # alias to $Counter::value
    $value++;
    return $value;
}

our $x מצהיר על alias לקסיקלי בתחום הנוכחי למשתנה החבילה $Pkg::x. המשתנה עצמו גלובלי. אם שתי חבילות מצהירות שתיהן our על $VERSION, כל אחת מקבלת את שלה - our מקדים בשם החבילה הנוכחית.

השתמש ב־our כאשר:

  • אתה אכן רוצה משתנה משותף ברמת החבילה.

  • ברצונך להשתיק תלונות של use strict 'vars'; על משתנה שאמור להיות גלובל של החבילה ($VERSION, @ISA, @EXPORT, aliases של %ENV, …).

אל תשתמש ב־our כדי לשתף מצב בין תת־שגרות באותו קובץ. בשביל זה קיים my בתחום הקובץ:

package Counter;

my $value = 0;                   # private to this package's file
sub increment { ++$value }
sub get       { $value   }

local - זמני בתיחום דינמי#

local אינו מצהיר על משתנה חדש. הוא שומר את הערך הנוכחי של משתנה קיים (בדרך כלל גלובלי), קובע ערך חדש, ומשחזר את הערך הישן כשהבלוק העוטף יוצא - אפילו בעת חריגה:

sub slurp {
    my ($path) = @_;
    open my $fh, '<', $path or die "open $path: $!";
    local $/;                    # slurp mode for this sub only
    return <$fh>;
}

ללא ה־local, ה־$/ של הקורא היה נדרס לצמיתות. איתו, השינוי מוגבל להיקף הדינמי של slurp - כולל כל תת־שגרה ש־slurp במקרה קורא לה.

local הוא הכלי הנכון כמעט לכל דריסה של משתנה מיוחד ($/, $\, $,, $_, $@, מטפלי אותות ב־%SIG). הוא לעתים רחוקות הכלי הנכון עבור משתני משתמש רגילים - אלה רוצים my.

לרשימה המלאה, ראה local ואת עמודי המשתנים מצב־שגיאה ו־קלט/פלט, המפרטים אילו משתנים מיוחדים רוצים local-יזציה ובאילו תבניות.

state - אתחל פעם אחת, שמור לעד#

use feature 'state';

sub counter {
    state $n = 0;                # initialised once, ever
    return ++$n;
}

counter();                       # 1
counter();                       # 2
counter();                       # 3

משתני state הם לקסיקלים השורדים בין קריאות. המאתחל רץ בפעם הראשונה שהבקרה מגיעה להצהרה; קריאות עוקבות מדלגות עליו.

state הוא הכלי הנכון עבור:

  • מטמונים ומונים לכל תת־שגרה כשעלות בניית הנתונים שולטת ואינך רוצה מפעל closure.

  • הקמה חד־פעמית התלויה בערכים שאינם זמינים בזמן הידור:

    sub compiled_re {
        my ($pattern) = @_;
        state %cache;
        $cache{$pattern} //= qr/$pattern/;
        return $cache{$pattern};
    }
    

    (הערה: זה ממטמן את התבנית הראשונה בלבד עם state ליטרלי של regex יחיד; צורת ה־hash מכלילה זאת.)

Closures: לכידת my מקיף#

תת־שגרה אנונימית לוכדת כל משתנה my (או state) מהתחום המקיף שלה שהיא מתייחסת אליו טקסטואלית:

sub make_adder {
    my ($n) = @_;
    return sub {
        my ($x) = @_;
        return $x + $n;          # captures $n
    };
}

my $add5 = make_adder(5);
my $add9 = make_adder(9);

$add5->(3);                      # 8
$add9->(3);                      # 12

כל קריאה ל־make_adder יוצרת $n חדש, וה־closure המוחזר מתייחס ל־$n שלו עצמו. שתי קריאות מפיקות שני מונים / מחברים / מכונות־מצב עצמאיים.

ההפתעה של closure־מעל־משתנה־לולאה#

my @subs;
for my $i (1, 2, 3) {
    push @subs, sub { $i };
}

print $_->(), "\n" for @subs;
# 1
# 2
# 3   - fresh $i each iteration: each closure sees its own

זוהי ההתנהגות הנכונה עם for my $i: כל איטרציה מצהירה $i חדש, כל closure לוכד את שלו.

הצורה הבאגית היא כש־$i מוצהר מחוץ ללולאה:

my $i;
my @subs;
for $i (1, 2, 3) {               # NO `my` - same $i every iteration
    push @subs, sub { $i };
}

print $_->(), "\n" for @subs;
# 3
# 3
# 3   - all three closures share the one $i, and its final value is 3

תמיד הצהר על משתני לולאה עם my. לכידת closure עוקבת אחר המשתנה, לא אחר הערך, ו“אותו משתנה“ לעומת ”משתנה שונה“ מוכרע באתר ה־my.

לכידת our וגלובלים של החבילה#

aliases של our חיים למשך כל החבילה, כך ש־closure הלוכד $Counter::value משתף מצב עם כל קטע קוד אחר הנוגע ב־$Counter::value. זה כמעט תמיד לא מה שאתה רוצה - closures מעל גלובלים של החבילה הם סימן שהתכנון רוצה my במקום.

אורך חיי ה־pad#

לכל קריאה פעילה לתת־שגרה יש pad, משבצת לכל לקסיקל מוצהר. ה־pad הוא מה ש־my כותב לתוכו ומה ש־closures מחזיקים אליו הפניה. כל עוד closure חי, כל pad שהוא לוכד חי - אפילו אם הקריאה המקורית חזרה מזמן. כך ה־$n של make_adder שורד את הקריאה ל־make_adder עצמו.

ראו גם#

  • my, our, local, state - עמודי פירוט ב־perlfunc עבור כל מצהיר.

  • @_ - המשתנה היחיד שתמיד נוכח בתת־שגרה ללא קשר למצהירים.

  • הצהרה - תת־שגרות אנונימיות וצורת ה־my sub הלקסיקלית.

  • רקורסיה - טרמפולינות מבוססות־closure ו־__SUB__.

  • משתנים מיוחדים - כמעט כל משתנה מיוחד רוצה local, לא my.