זרימת בקרה

sub#

הצהרה או הגדרה של תת־שגרה.

sub היא מילת המפתח המציגה תת־שגרה. עם NAME ו־BLOCK היא מגדירה תת־שגרה בעלת שם בחבילה הנוכחית. ללא BLOCK היא הצהרה־מקדימה. ללא NAME היא ביטוי sub אנונימי שמוערך להפניית קוד - sub { ... } היא הצורה היחידה של sub שהיא בפועל ביטוי; הצורות בעלות־השם הן הצהרות ואינן מחזירות דבר שימושי.

תקציר#

sub NAME BLOCK                         # named definition
sub NAME (PROTO) BLOCK                 # with prototype
sub NAME : ATTRS BLOCK                 # with attributes
sub NAME (PROTO) : ATTRS BLOCK         # prototype + attributes

sub NAME;                              # forward declaration
sub NAME (PROTO);                      # forward with prototype

my $ref = sub BLOCK;                   # anonymous, returns code ref
my $ref = sub (PROTO) : ATTRS BLOCK;   # anonymous with proto + attrs

use feature 'signatures';
sub NAME ($x, $y = 0, @rest) BLOCK     # named with signature
my $ref = sub ($x, $y) BLOCK;          # anonymous with signature

מה מוחזר#

הצורות בעלות־השם (sub NAME BLOCK, sub NAME (PROTO) BLOCK, וכו«) הן הצהרות. הן מתקינות את תת־השגרה אל טבלת הסמלים של החבילה הנוכחית בזמן הידור ואינן תורמות דבר לביטוי הסובב. כתיבת my $x = sub foo { 1 }; היא טעות נפוצה - אלו שתי הצהרות מודבקות יחד, ו־$x נשאר לא־מוגדר.

הצורה האנונימית sub BLOCK היא ביטוי. הוא מוערך - בנקודה שבה הביטוי מגיע בזמן ריצה, לא בזמן הידור - להפניית קוד הסוגרת על המשתנים הלקסיקליים בתחום. אותו ביטוי sub { ... } שמוערך פעמיים מפיק שתי הפניות קוד נבדלות שאינן חולקות דבר:

my @refs = map { sub { $_ } } 1..3;     # three independent closures

הצהרות מקדימות (sub NAME; ללא בלוק) אינן עושות דבר בזמן ריצה ואינן מחזירות דבר. תכליתן היא להודיע לפרסר על השם כך שקריאות מאוחרות יותר יוכלו להיפרסר ללא סוגריים.

מצב גלובלי שהוא נוגע בו#

  • החבילה הנוכחית (הנקבעת על־ידי package) קובעת את השם המוסמך במלואו של תת־שגרה בעלת שם. sub foo { } בתוך package Acme; מתקין את Acme::foo, לא את main::foo.

  • @_ בתוך הגוף מחזיק את רשימת הארגומנטים (alias לסקלרים של הקורא, פרט למקרה שחתימה בתוקף - ראו חתימות להלן). השמה ל־@_ כשלם שוברת את ה־aliasing עבור שאר הקריאה.

  • איברי $_[N] הם aliases לסקלרים של הקורא. שינוי $_[0] משנה את משתנה הקורא; העברת ליטרל ושינוי $_[0] לאחר מכן היא שגיאה קטלנית.

  • wantarray בתוך הגוף משקף את ההקשר שבו תת־השגרה נקראה.

הגדרה בעלת שם#

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

print greet("world"), "\n";            # Hello, world

הגוף מהודר בזמן הידור. השם נראה מנקודה זו בקובץ ואילך; הפניות מקדימות בתוך אותה חבילה עובדות משום ש־Perl דוחה קריאות בסגנון מתודה ו־bareword.

הגדרה־מחדש של תת־שגרה בעלת שם בזמן ריצה (eval "sub foo { ... }" או sub foo { } שני באותה חבילה) מחליפה את ההגדרה הקודמת ופולטת אזהרת Subroutine foo redefined תחת use warnings. ניתן לדכא ב־no warnings 'redefine' לתחום ההגדרה־מחדש.

sub אנונימי וסגירות#

sub make_counter {
    my $n = 0;
    return sub { ++$n };
}

my $c = make_counter();
print $c->(), $c->(), $c->(), "\n";    # 123

ה־sub הפנימי לוכד את $n מהתחום המקיף שלו. כל קריאה ל־make_counter יוצרת $n טרי וסגירה טריה עליו.

my @doublers = map { my $k = $_; sub { $k * 2 } } 1..3;
print $doublers[2]->(), "\n";          # 6

שימו לב ל־my $k = $_ הביניים - סגירות לוכדות משתנים, לא ערכים, ו־$_ משותף בין איטרציות map. ללא ההעתקה, כל סגירה הייתה רואה את הערך האחרון של $_.

חתימות#

עם use feature 'signatures' (מופעל כברירת מחדל תחת use v5.36 ואילך), רשימת הפרמטרים מופיעה בין השם לבלוק, וארגומנטים נכרכים אוטומטית למשתנים לקסיקליים:

use feature 'signatures';

sub add ($x, $y) { $x + $y }

sub greet ($name, $greeting = "Hello") {
    return "$greeting, $name";
}

sub log_all ($level, @msgs) {
    print "[$level] $_\n" for @msgs;
}

צורות חתימה:

  • $x - פרמטר מיקומי נדרש.

  • $x = EXPR - מיקומי אופציונלי עם ברירת מחדל; EXPR מוערך בכל קריאה כשהארגומנט חסר.

  • $x = undef - אופציונלי, ברירת מחדל היא undef (שימושי לתיעוד שמשבצת היא ניתנת ל־null ולא מושמטת).

  • @rest / %rest - slurpy, צורך את כל הארגומנטים הנותרים.

  • $ / @ / % - מציין־מקום ללא שם, טוען מספר־ארגומנטים ללא כריכת משתנה.

קריאה עם מספר־ארגומנטים שגוי מקרקרת: Too many arguments for subroutine או Too few arguments for subroutine.

תחת חתימה, @_ עדיין קיים אך גישה אליו היא אזהרה תחת use warnings 'experimental::args_array_with_signatures' ב־Perl ישנים יותר ולא מומלצת ב־5.42. יש להשתמש במשתני החתימה במקום.

פרוטוטיפים#

sub mypush (\@@) {
    my $aref = shift;
    push @$aref, @_;
}

my @a = (1, 2);
mypush @a, 3, 4;                       # @a is now (1, 2, 3, 4)

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

פרוטוטיפים אינם עושים דבר כאשר תת־השגרה נקראת דרך &name(...) או דרך הפניית קוד. אם רוצים חתימה, יש להשתמש ב־use feature 'signatures'; פרוטוטיפים וחתימות יכולים להתקיים יחד עם המאפיין :prototype(...):

use feature 'signatures';
sub each_pair :prototype(\@) ($aref) { ... }

מאפיינים#

מאפיינים מופיעים אחרי : ומשנים כיצד תת־השגרה מהודרת או נרשמת:

sub critical :lvalue { $state }        # usable on the left of =
sub noop     :method { }               # marked as a method (for some tools)

מאפייני ליבה נפוצים: lvalue, method, prototype(...), const. מודולים כגון Attribute::Handlers מאפשרים לחבילה להגדיר משלה. מאפיינים לא־ידועים הם שגיאת זמן הידור.

הצהרה מקדימה#

sub later;                             # announce the name
plan later(3);                         # now parses without parens
sub later { ... }                      # defined later in the file

הצהרה מקדימה שימושית רק כדי לומר לפרסר ש־later היא תת־שגרה כך ש־later ARG יתפרסר כקריאה ולא כמבנה עצם־עקיף או ביטוי פסיק. היא אינה נדרשת אם קוראים עם סוגריים.

דוגמאות#

תת־שגרה בעלת שם, פירוק ארגומנטים מפורש:

sub area {
    my ($w, $h) = @_;
    return $w * $h;
}
print area(3, 4), "\n";                # 12

sub אנונימי המועבר לפונקציה מסדר גבוה:

my @lengths = map { sub { length shift } }->(@_),
              ("alpha", "beta", "gamma");   # not quite - see below

שימושי יותר: העברת sub ישירות:

use List::Util qw(first);
my $first_even = first { $_ % 2 == 0 } 1, 3, 4, 7;   # 4

החזרה מובלעת - הערך של הביטוי האחרון מוחזר:

sub double { $_[0] * 2 }               # no return needed
print double(21), "\n";                # 42

החזרה רגישה להקשר:

sub names {
    my @n = qw(alice bob carol);
    return wantarray ? @n : scalar @n;
}

my @all = names();                     # ("alice","bob","carol")
my $n   = names();                     # 3

שינוי ארגומנטי הקורא דרך aliasing של @_:

sub upcase_in {
    for (@_) { $_ = uc }
}
my $s = "hello";
upcase_in($s);
print $s, "\n";                        # HELLO

רקורסיה עם __SUB__ - הפניה לתת־השגרה הנוכחית ללא לתת לה שם:

use feature 'current_sub';
my $fact = sub {
    my $n = shift;
    $n < 2 ? 1 : $n * __SUB__->($n - 1);
};
print $fact->(5), "\n";                # 120

מקרי קצה#

  • הגדרת sub בעלת שם היא הצהרה, לא ביטוי. היא אינה תורמת דבר לביטוי המקיף שלה. my $x = sub foo {}; מתפרסר כשתי הצהרות; $x נגמר undef.

  • הצורות sub NAME BLOCK הן זמן הידור. גוף הבלוק מפורסר ומהודר כאשר הקובץ המקיף או ה־eval מהודרים. תופעות לוואי בתוך הבלוק רצות רק כאשר תת־השגרה נקראת, לא כאשר היא מוגדרת. בניגוד לכך, sub BLOCK (אנונימי) מוערך בזמן ריצה בכל פעם שמגיעים לביטוי, ומפיק סגירה חדשה בכל פעם.

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

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

  • השמה כוללת ל־@_ שוברת aliasing. @_ = @_ או @_ = (1, 2) מחליפים את המערך ומאוחר יותר $_[0] = X לא מגיע יותר אל הקורא.

  • העברת ליטרל ואז שינוי $_[0] היא קטלנית. upcase_in("x") מת עם Modification of a read-only value attempted משום ש־$_[0] הוא alias לליטרל.

  • הגדרה־מחדש של sub פולטת Subroutine X redefined תחת use warnings. עטיפת הגדרה־מחדש דורשת no warnings 'redefine' לתחום:

    no warnings 'redefine';
    *Foo::bar = sub { ... };
    
  • sub בתוך בלוק BEGIN מוגדר בזמן הידור של היחידה המקיפה, כמו כל הצהרת sub; עטיפת ה־BEGIN אינה משנה את התזמון של ההצהרה עצמה.

  • פח של פרוטוטיפ אחרי רווח. sub foo ($$) עם use feature 'signatures' מתפרסר כחתימה, לא כפרוטוטיפ. כדי לצרף פרוטוטיפ תחת חתימות, יש להשתמש ב־sub foo :prototype($$) ($x, $y) { ... }.

הבדלים מהמעלה־הזרם#

תאימות מלאה עם Perl 5.42 מהמעלה־הזרם.

ראו גם#

  • return - חזרה מפורשת מתת־שגרה; בלעדיו ערך הביטוי האחרון מוחזר

  • wantarray - בחינת הקשר הקריאה (רשימה / סקלר / void) מתוך הגוף

  • caller - מידע על מי קרא לתת־שגרה זו ומהיכן

  • prototype - קריאה חזרה של הפרוטוטיפ של sub בעל שם או אנונימי

  • __SUB__ - הפניה לתת־השגרה הנוכחית ללא לתת לה שם; הכלי לרקורסיה אנונימית

  • my - הדרך הרגילה לפרק את @_ ללקסיקליים בעלי שם כשלא משתמשים בחתימות

  • method - הצהרת מתודה בתחביר מחלקה; השקול של sub למתודות תחת use feature 'class'