do#
ביצוע בלוק קוד או הרצת קובץ מקור Perl כאילו היה חלק מהתוכנית הנוכחית.
do חובש שני כובעים בלתי־קשורים שחולקים רק מילת מפתח. do BLOCK מקבץ הצהרות לכדי ביטוי יחיד ומחזיר את הערך של האחרונה - כלי תחבירי, לא פונקציה. do EXPR מתייחס למחרוזת כאל שם קובץ ומהדר־ומריץ את הקובץ בזמן ריצה. יש לקרוא את הסעיף שתואם את הצורה שרואים; ערבוב ביניהם הוא מקור הבלבול הנפוץ ביותר סביב מילת מפתח זו.
תקציר#
do BLOCK # group statements, yield last value
do EXPR # load and run a Perl file
do { ... } while CONDITION; # post-test loop idiom
מה מוחזר#
do BLOCK - קיבוץ הצהרות לכדי ביטוי אחד#
do BLOCK אינו פונקציה. זהו מבנה תחבירי שהופך רצף הצהרות תחום בסוגריים מסולסלים לכדי איבר יחיד שערכו הוא הערך של ההצהרה האחרונה. יש להישען עליו כאשר השפה רוצה ביטוי אך יש לכם מספר הצהרות להרצה:
my $x = do {
my $tmp = fetch_raw();
$tmp =~ s/\s+\z//;
lc $tmp;
}; # $x = cleaned, lower-cased value
כאשר do BLOCK מותאם על־ידי while או until מסיים, הבלוק רץ פעם אחת לפני שהתנאי נבדק - זהו אידיום לולאת הבדיקה־בסוף הסטנדרטי:
do {
$line = <$fh>;
} while defined $line && $line !~ /^END\b/;
על כל הצהרה אחרת, while ו־until כמתאמי הצהרה בודקים את התנאי ראשון. do הוא היוצא־מן־הכלל.
next, last, redo אינם עובדים בתוך do BLOCK#
do BLOCK אינו לולאה, גם כאשר נספח אליו while או until. מילות מפתח של בקרת לולאה אינן רואות אותו:
do {
last if $done; # WRONG - not a loop
} while $more;
אם נדרשת בקרת לולאה, יש לעטוף את הגוף בבלוק חשוף, שהוא כן תחום הניתן ללולאה:
{
last if $done;
redo if $retry;
}
או להשתמש בלולאת while מפורשת.
return בתוך do BLOCK חשוף#
return ב־do BLOCK חוזר מתת־השגרה המקיפה, לא מהבלוק עצמו. זה לעתים מה שרוצים ולעתים באג - בכל מקרה, יש לדעת איזה מהם כותבים:
sub classify {
my $n = shift;
my $kind = do {
return "zero" if $n == 0; # returns from classify(), not the do
$n > 0 ? "pos" : "neg";
};
"$kind ($n)";
}
כדי להניב ערך מהבלוק בלי לחזור מתת־השגרה, פשוט יש לתת לביטוי האחרון להיות הערך - זוהי כל התכלית של do BLOCK.
do EXPR - טעינה והרצה של קובץ מקור Perl#
do EXPR מעריך את EXPR, מתייחס לתוצאה כאל שם קובץ, קורא את הקובץ, מהדר אותו, ומריץ אותו בחבילה הנוכחית. זה דומה ל־, אך לא בדיוק זהה ל־,
eval `cat stat.pl`;
צורת do אינה מריצה תהליך חיצוני, שומרת על שם הקובץ המקורי עבור הודעות שגיאה, ואינה יכולה לראות לקסיקליים בתחום המקיף - מה ש־eval STRING יכול. כמו eval STRING, הקובץ מפורסר מחדש בכל קריאה, כך ש־do FILE בתוך לולאה הוא כמעט תמיד שגוי.
פתרון נתיב ו־@INC#
שם הקובץ מתפרש כך:
נתיבים מוחלטים (
/foo/stat.pl), נתיבים המתחילים ב־./, ונתיבים המתחילים ב־../בשימוש כפי שהם.כל נתיב יחסי אחר מחופש לאורך
@INC, ובהצלחה הנתיב שנמצא נרשם ב־%INCתחת המפתח שהעברתם.
do '/etc/myapp/stat.pl'; # exact file
do './stat.pl'; # exact file, current directory
do 'stat.pl'; # search @INC
do 'MyApp/config.pl'; # search @INC
מאז Perl 5.26 . אינו ב־@INC כברירת מחדל, כך ש־do 'stat.pl' חשוף אינו נופל יותר חזרה לספרייה הנוכחית. אם הקובץ לא נמצא, Perl פולט את הרמז:
do "stat.pl" failed, '.' is no longer in @INC;
did you mean do "./stat.pl"?
יש להעביר ./stat.pl כשמתכוונים לספרייה הנוכחית.
ערך מוחזר ודיווח שגיאות#
ל־do EXPR יש שלוש תוצאות שיש להבחין ביניהן:
תוצאה | החזרה | ||
|---|---|---|---|
הקובץ הודר ורץ | ערך הביטוי האחרון |
| ללא שינוי |
הקובץ נקרא אך לא הודר | שגיאת הידור | עשוי להיקבע גם הוא | |
הקובץ לא ניתן לקריאה |
| שגיאת קלט/פלט |
מכיוון שכישלון הידור יכול לקבוע גם את $! דרך אגב, תמיד יש לבדוק את $@ ראשון:
my $return;
for my $file ("/etc/myapp.rc", "$ENV{HOME}/.myapprc") {
unless ($return = do $file) {
warn "couldn't parse $file: $@" if $@;
warn "couldn't do $file: $!" unless defined $return;
warn "couldn't run $file" unless $return;
}
}
שלוש קריאות ה־warn מתאימות לשלוש צורות הכישלון לעיל ולמקרה הרביעי - הקובץ רץ אך הביטוי האחרון שלו היה שקר.
מדוע לא להשתמש ב־do FILE עבור מודולים#
לטעינת קוד ספריה, יש להשתמש ב־require או ב־use במקום ב־do EXPR. require שומר במטמון דרך %INC (כך שקובץ נטען לכל היותר פעם אחת), זורק חריגה בכישלון במקום להחזיר undef, ומשתלב עם הוואי @INC. use מוסיף טעינה בזמן הידור וטיפול בייבוא מעליו. do EXPR קיים עבור קובצי הגדרה והכללת סקריפט ad-hoc - לא עבור ספריות.
דוגמאות#
קיבוץ הצהרות לכדי ביטוי:
my $config = do {
open my $fh, "<", $path or die $!;
local $/;
<$fh>;
}; # $config = whole file as one string
לולאת בדיקה־בסוף עם do … while:
my $n;
do {
print "enter a positive number: ";
chomp($n = <STDIN>);
} until defined $n && $n =~ /\A\d+\z/ && $n > 0;
קריאת קובץ הגדרה בתחביר Perl, עם טיפול שגיאות תלת־כיווני מלא:
my $cfg = do '/etc/myapp/config.pl';
if (!defined $cfg) {
die $@ ? "config parse error: $@"
: "config read error: $!";
}
die "config did not return a true value" unless $cfg;
בחירת ערך מותנית כאשר כל ענף הוא מספר הצהרות:
my $greeting = do {
if ($user) {
my $name = $user->display_name;
"hello, $name";
}
else {
"hello, stranger";
}
};
לוקליזציה זמנית שחייבת להשתרע על מספר הצהרות ועדיין להניב ערך:
my $dump = do {
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Indent = 1;
Data::Dumper::Dumper(\%state);
};
מצב גלובלי שהוא נוגע בו#
@INC- מחופש כאשרdo EXPRנקרא עם נתיב יחסי אחר מ־./…או../….%INC- מעודכן בנתיב הפתור כאשרdo EXPRמצליח בחיפוש@INC.$@- נקבע על־ידיdo EXPRבכישלון הידור; מנוקה בהצלחה.$!- נקבע על־ידיdo EXPRבכישלון קלט/פלט (הקובץ לא ניתן לקריאה).$0,__FILE__,__LINE__- שם הקובץ המהודר כעת נקבע לקובץ הנטען בעודdo EXPRמפרסר אותו, כך שאזהרות והודעותdieמצביעות על הקובץ הנכון.
do BLOCK אינו נוגע בגלובליים; זהו מבנה תחבירי טהור.
מקרי קצה#
do BLOCKאינו לולאה.next,last, ו־redoבתוךdo … whileאינם חלים על הבלוק. יש להשתמש בבלוק חשוף או בלולאתwhileאמיתית.returnב־do BLOCKחוזר מתת־השגרה המקיפה. לא מהבלוק. אין דרך להחזיר ערך מ־do BLOCKמלבד הפיכתו לביטוי האחרון שמוערך.מתאמי הצהרה בודקים־מראש בכל מקום אחר.
STATEMENT while CONDבודק ראשון;do BLOCK while CONDבודק אחרי ההרצה הראשונה. זוהי חריגת שפה מכוונת.נתיבים יחסיים ללא
./מחפשים@INC. מאז 5.26,.אינו ב־@INC, כך ש־do 'stat.pl'אינו מציין יותר ״קובץ בספרייה הנוכחית״; יש להעביר./stat.plאם זו הייתה הכוונה.do EXPRאינו יכול לראות לקסיקליים מקיפים. הקובץ מפורסר כאילו הוא ברמה העליונה. אם נדרשת גישה ללקסיקליים, יש להשתמש ב־eval STRING- עם אזהרות ה־taint וההזרקה הרגילות.הקובץ מפורסר מחדש בכל קריאה.
do EXPRאינו נשמר במטמון דרך%INCכפי ש־requireנשמר. קריאה לו בתוך לולאה קוראת ומהדרת מחדש את הקובץ בכל איטרציה.יש לבדוק את
$@לפני$!. כישלון הידור יכול לקבוע את$!גם הוא כתופעת לוואי; שגיאת ההידור ב־$@היא זו שבאמת רוצים לדווח עליה.קובץ שמהדר אך מחזיר ערך שקר הוא דו־משמעי.
unless ($ret = do $file) { … }נורה גם עבור ״הקובץ נכשל״ וגם עבור ״הקובץ רץ והחזיר0״. אם מותר לקובץ ההגדרה שלכם להסתיים בערך שקר, יש להשתמש ב־definedוב־$@במפורש במקום בבדיקת אמת על ערך ההחזרה.doללא ארגומנט הוא שגיאת תחביר. שתי הצורות דורשות איבר: בלוק או ביטוי.
הבדלים מהמעלה־הזרם#
תאימות מלאה עם Perl 5.42 מהמעלה־הזרם.
ראו גם#
eval-eval BLOCKלוכד חריגות ללא קריאת קובץ;eval STRINGמפרסר קוד שרירותי ויכול לראות לקסיקליים מקיפים, בניגוד ל־do EXPRrequire- טעינת קובץ ספריה עם שמירה במטמון של%INCוחריגה אוטומטית בכישלון; הכלי הנכון עבור מודוליםreturn- שימו לב ש־returnבתוךdo BLOCKחוזר מתת־השגרה המקיפה, לא מהבלוק@INC- נתיב חיפוש מודולים שאליוdo EXPRפונה עבור שמות קבצים יחסיים%INC- רושם את הנתיב הפתור לאחרdo EXPRמוצלח שחיפש ב־@INC$@- שגיאת זמן הידור מ־do EXPR$!- שגיאת קלט/פלט מ־do EXPRכאשר הקובץ לא ניתן לקריאה