SUB#
מחזיר הפניה לתת־השגרה המתבצעת כעת.
__SUB__ הוא טוקן ההפניה־העצמית: בתוך sub, הוא מוערך להפניית קוד המצביעה על אותו sub עצמו, מבלי שה־sub יצטרך לדעת את שמו. מחוץ לכל sub, הוא undef. הסיבה הנפוצה לפנות אליו היא רקורסיה אנונימית - sub המאוחסן בלקסיקלי או המועבר כ־callback זקוק לדרך לקרוא לעצמו שאינה תלויה בשם מוסמך־חבילה, ו־__SUB__ הוא הדרך הזו.
תקציר#
__SUB__
__SUB__->(@args)
goto __SUB__
אינו לוקח ארגומנטים ואין לו צורת סוגריים - זהו טוקן של זמן הידור, לא קריאת פונקציה.
מה מקבלים בחזרה#
הפניית קוד (אותו דבר ש־\&name היה נותן עבור sub בעל שם), או undef כאשר מוערך מחוץ לכל תת־שגרה. ההפניה היא ל־sub של זמן ריצה, כולל כל closure מעל לקסיקליים שנלכדו בזמן יצירת ה־sub, כך שקריאה אליו רקורסיבית רואה את אותה סביבה לכודה.
my $fact = sub {
my $n = shift;
$n < 2 ? 1 : $n * __SUB__->($n - 1);
};
print $fact->(5); # 120
צורת הקריאה היא הפעלת coderef רגילה: __SUB__->(@args) או &{__SUB__}(@args). שום ארגומנט אינו מועבר במשתמע; הרקורסיה חייבת להעביר אותם הלאה.
מדוע לא פשוט להשתמש בשם של ה־sub#
subs בעלי שם כבר יכולים לבצע רקורסיה לפי שם - factorial(...) בתוך sub factorial עובד. __SUB__ מוצדק בשלושה מצבים שבהם שם אינו זמין, אינו אמין, או יקר:
subs אנונימיים. ל־sub אנונימי המוקצה ללקסיקלי אין שם מוסמך־חבילה יציב. מעקף נפוץ לפני 5.16 היה לכידת הפניה ל־
\&nameאו שימוש בתכסיסיY-combinator;__SUB__מחליף את שניהם.subs המועברים כ־callbacks. sub שנמסר ל־API אחר (משווה sort, מטפל אירועים, צעד איטרטור) עשוי להיקרא בחזרה לפני שיש לו שם הנראה באתר הרקורסיה.
רקורסיית tail call באמצעות
goto.goto __SUB__משתמש מחדש במסגרת הקריאה הנוכחית וקופץ לראש אותו sub עם כל@_שהכנת, מה שחשוב לרקורסיה עמוקה שאחרת הייתה מפוצצת את המחסנית.
מצב גלובלי שהוא נוגע בו#
__SUB__ קורא את מסגרת הקריאה המתבצעת כעת - אותה מסגרת ש־caller בוחן. הוא אינו קורא או משנה משתנים מיוחדים כלשהם.
דוגמאות#
factorial אנונימי, המקרה מספר הלימוד:
my $fact = sub {
my $n = shift;
$n < 2 ? 1 : $n * __SUB__->($n - 1);
};
print $fact->(6); # 720
סכום tail-רקורסיבי עם goto __SUB__ - ללא צמיחת מחסנית לכל איטרציה, כי goto משתמש מחדש במסגרת הנוכחית:
my $sum = sub {
my ($acc, @rest) = @_;
return $acc unless @rest;
@_ = ($acc + shift @rest, @rest);
goto __SUB__;
};
print $sum->(0, 1..1_000_000); # 500000500000
מעבר על מבנה נתונים מקונן ללא מתן שם להולך:
my $count_leaves = sub {
my $node = shift;
return 1 unless ref $node eq 'ARRAY';
my $n = 0;
$n += __SUB__->($_) for @$node;
return $n;
};
print $count_leaves->([1, [2, 3], [[4], 5]]); # 5
מחוץ לכל sub, __SUB__ הוא undef:
print defined(__SUB__) ? "yes" : "no"; # no
נלכד, לא נפתר־בעת־קריאה. __SUB__ מוערך בכל פעם שהוא רץ, במסגרת שבה הוא רץ - כך שב־sub מקונן הוא מתייחס ל־sub הפנימי:
sub outer {
my $inner = sub { __SUB__ }; # inner's own coderef
return $inner->();
}
my $ref = outer();
print $ref == $ref; # 1 - stable coderef
מקרי קצה#
דורש את התכונה
current_sub.__SUB__מותנה ב־use feature 'current_sub', מופעל כברירת מחדל תחתuse v5.16או כל חבילת גרסה מאוחרת יותר. קוד המכוון לרמות תחביר ישנות יותר חייב להצטרף במפורש, או להוסיף קידומתCORE::לטוקן כ־CORE::__SUB__.אין צורת סוגריים.
__SUB__()הוא שגיאת תחביר - הטוקן אינו פונקציה, הוא מילת מפתח המוערכת ל־coderef. קרא לתוצאה באמצעות__SUB__->()או&{__SUB__}().מחוץ לכל sub, כולל בתחום הקובץ ובתוך בלוקי
BEGIN,UNITCHECK,CHECK,INIT, ו־END,__SUB__מחזירundef. קריאה אליו (__SUB__->()) במיקומים אלה מתה עםCan't use an undefined value as a subroutine reference.בתוך בלוקי
eval,__SUB__מתייחס ל־sub העוטף, לא ל־eval-eval { ... }אינו יוצר מסגרת sub חדשה למטרה זו. בתוךeval EXPRחל אותו כלל: הטוקן נפתר ל־sub החיצוני.בתוך בלוק קוד של regex (
/(?{...})/או/(??{...})/), התנהגות__SUB__כפופה לשינוי - upstream מתעד זאת כלא־יציב. אל תסתמך על מה שהוא מחזיר שם.זהות בין קריאות. שתי הערכות של
__SUB__בתוך אותו sub מניבות את אותו coderef (==משווה אמת). בין closures שונים שנעשו מאותוsub { ... }, כל closure רואה coderef משלו - אחד לכל סביבה לכודה.goto __SUB__לעומת__SUB__->(). צורתgotoמשתמשת מחדש במסגרת הקריאה הנוכחית ומחליפה את@_בכל מה שהקצית לפני ה־goto; היא אינה מגדילה את מחסנית הקריאות. צורת קריאת coderef יוצרת מסגרת חדשה בכל פעם, כך שהיא פוגעת בגבול עומק הרקורסיה על קלטים רקורסיביים עמוקים.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
sub- מגדיר את תת־השגרה שאת ההפניה אליה__SUB__מחזיר; המקום הראשון לחפש בו כאשר רקורסיה אנונימית היא הכלי למשימהgoto- מצטרף ל־__SUB__עבור רקורסיית tail call באמצעותgoto __SUB__, האידיום שמונע צמיחת מחסניתcaller- בחן את אותה מסגרת קריאה מבחוץ (חבילה, קובץ, שורה) במקום לתפוס הפניה אליהref- סווג את מה ש־__SUB__החזיר; תמיד"CODE"בתוך sub, מחרוזת ריקה מחוץreturn- הדרך הרגילה לצאת מה־sub שאת ההפניה אליו__SUB__מחזיק; רלוונטי בעת בחירה בין רקורסיה ל־tail call באמצעותgoto __SUB__