זרימת בקרה

wantarray#

מדווח על הקשר הקריאה של התת־שגרה המתבצעת כעת.

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

תקציר#

wantarray

אינו לוקח ארגומנטים ואין לו צורת סוגריים - זוהי מילת מפתח, לא קריאת פונקציה.

מה מקבלים בחזרה#

wantarray מחזיר אחד מבדיוק שלושה ערכים, התואמים לשלושת ההקשרים ש־Perl מבחין:

ערך החזרה

הקשר של הקורא

תחביר קורא טיפוסי

אמת (1)

רשימה

my @x = f(); (f())[0]

שקר ("")

סקלר

my $x = f(); if (f()) {…}

undef

void

f(); כמשפט

ההבחנה התלת־כיוונית היא הסיבה שהפונקציה ”הייתה צריכה להיקרא wantlist()“: ההחזרה הנראית בוליאנית היא למעשה תלת־מצבית, ו־defined wantarray הוא הבדיקה הנכונה ל“האם הקורא רצה משהו בכלל“.

הניב הקנוני#

הדפוס ש־wantarray קיים בעבורו הוא תת־שגרה המתאימה את צורת ההחזרה שלה לקורא:

sub records {
    my @rows = heavy_query();
    return wantarray ? @rows : "@rows";
}

my @r = records();          # gets the list
my $s = records();          # gets a space-joined string
records();                  # still runs heavy_query - see below

יש לזווג זאת עם יציאה מוקדמת כשהקורא לא ביקש דבר:

sub records {
    return unless defined wantarray;    # caller wrote `records();`
    my @rows = heavy_query();           # skipped in void context
    return wantarray ? @rows : "@rows";
}

defined wantarray הוא השומר של הקשר void. wantarray לבדו אינו - הוא שקר גם בסקלר וגם ב־void, ו־return מוקדם על wantarray שקר ישבור כל קורא של הקשר סקלר.

ההקשר מופץ, לא מפורש מחדש#

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

sub ctx { wantarray ? "list" : defined wantarray ? "scalar" : "void" }

my @a = ctx();      # "list"
my $s = ctx();      # "scalar"
ctx();              # "void"
print ctx(), "\n";  # "list"   - print's LIST is list context
scalar ctx();       # "scalar" - scalar() forces scalar context

יש לשים לב לשני האחרונים: print כופה הקשר רשימה על ארגומנטיו, ו־scalar כופה במפורש הקשר סקלר. אף אחד מהם אינו עוסק במה ש־ctx עושה עם התוצאה.

קריאות מהקשר void#

תת־שגרה הנקראת כמשפט חשוף רצה בהקשר void, ו־wantarray מחזיר undef בתוך אותה תת־שגרה. זה אינו מדורג: תת־שגרה הנקראת מתוך תת־שגרה בהקשר void רואה את ההקשר ש־הקורא של עצמה כפה.

sub inner { defined wantarray ? "wanted" : "void" }
sub outer { my $x = inner(); return $x }

outer();            # outer is void; inner is scalar → "wanted"

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

עובד גם בתוך eval#

wantarray מדווח גם על ההקשר של בלוק eval או eval EXPR, לא רק על תת־שגרות בעלות שם:

my @x = eval { wantarray ? (1, 2, 3) : "scalar" };  # (1,2,3)
my $x = eval { wantarray ? (1, 2, 3) : "scalar" };  # "scalar"
eval { defined wantarray ? 1 : 0 };                 # void → 0

זה אותו מנגנון - eval הוא מסגרת קריאה באותו מובן שתת־שגרה היא.

היכן ל־wantarray אין תשובה שימושית#

תוצאת wantarray היא לא מוגדרת במקומות אלה, וקוד לא צריך להסתמך על ערך מסוים:

  • רמת העל של קובץ (מחוץ לכל תת־שגרה).

  • בתוך בלוק BEGIN, UNITCHECK, CHECK, INIT, או END.

  • בתוך מתודת DESTROY.

יש להתייחס ל־wantarray כמשמעותי רק בתוך תת־שגרה רגילה או eval. בכל מקום אחר, יש לעצב את הקוד כך שהתשובה לא תהיה משנה.

דוגמאות#

Getter המחזיר את הרשימה המלאה לקוראי רשימה, את הספירה לקוראי סקלר, ולא עושה דבר בהקשר void:

sub warnings {
    return unless defined wantarray;
    my @w = collect_warnings();
    return wantarray ? @w : scalar @w;
}

my @all   = warnings();     # every warning
my $count = warnings();     # just the number
warnings();                 # collect_warnings() is not called

תת־שגרה המסרבת להיקרא בהקשר void כי ערך ההחזרה הוא כל העניין:

sub must_use {
    defined wantarray
        or croak "must_use: return value must be used";
    return compute();
}

תת־שגרה המנפיקה איבר אחד לקריאה בהקשר סקלר ואת כל האצווה בהקשר רשימה - צורה שכמה ממשקי API בסגנון איטרטור חושפים:

sub next_batch {
    state @queue;
    @queue = refill() unless @queue;
    return wantarray ? splice(@queue) : shift @queue;
}

מקרי קצה#

  • אין סוגריים, אין ארגומנטים. wantarray() מנותח אך הסוגריים הריקים הם רעש; מילת המפתח אינה לוקחת דבר. wantarray $x היא שגיאת תחביר, לא קריאה עם ארגומנט.

  • לא הקשר הקורא עבור אופרטורים. wantarray מדווח רק על הקשר מסגרת הקריאה. ביטוי כמו $sub->() + 1 מציב את הקריאה בהקשר סקלר; wantarray בתוך $sub רואה סקלר, כצפוי. אין דרך לשאול ”האם נקראתי בתוך ביטוי אריתמטי“ - רק רשימה / סקלר / void.

  • return מכבד את אותו הקשר. return @list בהקשר סקלר מניב את האיבר האחרון, לא את הספירה - מכיוון ש־return עצמו רואה את הקשר הקורא. יש להשתמש ב־return wantarray ? @list : scalar @list כשרוצים את הספירה עבור קוראי סקלר.

  • הקשר בוליאני הוא הקשר סקלר. if (f()) קורא ל־f בהקשר סקלר; wantarray מחזיר שקר שם, לא undef.

  • השמת רשימה לרשימה ריקה היא עדיין הקשר רשימה. () = f(); הוא הקשר רשימה למרות שהתוצאה מושלכת. wantarray מחזיר אמת. זה שימושי לכפיית הקשר רשימה על תת־שגרה שתופעות הלוואי שלה תלויות בו.

  • wantarray אינו זמין באותה דרך לקוראי XS. XSUB בודק הקשר דרך GIMME_V; תת־שגרת Perl הנקראת מתוך XSUB רואה את ההקשר שה־XSUB קבע לקריאה.

  • Prototypes אינם משנים את wantarray. prototype ($) כופה את ההקשר של ארגומנט, לא את הקשר הקריאה של התת־שגרה עצמה.

הבדלים מהמקור#

תואם במלואו ל־Perl 5.42 במקור.

ראו גם#

  • return - מכבד את אותו הקשר רשימה/סקלר/void ש־wantarray מדווח עליו; השניים מתוכננים לשימוש יחד

  • caller - בדיקת מחסנית הקריאה עצמה (חבילה, קובץ, שורה, ובצורת שלושת הארגומנטים שלו, פרטי הקשר נוספים כולל האם wantarray יחזיר אמת שם)

  • scalar - כופה הקשר סקלר על ביטוי, שזה מה שגורם ל־wantarray להחזיר שקר בנקרא

  • eval - קובע מסגרת קריאה ש־wantarray מדווח גם על הקשרה