grep#
מסנן רשימה לאלמנטים שבהם הבלוק או הביטוי הם אמת.
grep עובר על LIST משמאל לימין, מעריך את BLOCK או EXPR פעם אחת לכל אלמנט כאשר $_ משויך ככינוי לאותו אלמנט, ומחזיר את האלמנטים שעבורם הבדיקה הניבה ערך אמת. זהו מסנן רשימות, לא לולאה - הרשימה שאתם מקבלים בחזרה היא תת־קבוצה של הרשימה שהעברתם, בסדר המקורי.
תקציר#
grep BLOCK LIST
grep EXPR, LIST
my @kept = grep { COND } @list;
my @match = grep /pattern/, @list;
שתי הצורות שקולות בעוצמה; BLOCK הוא המקרה הכללי, ו־EXPR הוא נוחות לבדיקות חד־ביטויות. כאשר EXPR הוא regex חשוף (/.../, m/.../, או qr/.../), הוא מותאם במובלע אל $_ - זהו הניב שמאחורי grep /^#/, @lines.
מה מקבלים בחזרה#
הקשר רשימה: הרשימה המסוננת, בסדר המקורי, כאשר כל אלמנט מוחזר הוא alias לתוך רשימת המקור (ראו מצב גלובלי ו־מקרי קצה).
הקשר סקלר: ספירת האלמנטים שעבורם הבדיקה הייתה אמת. לא בוליאני - זהו גודל מה שהקשר הרשימה היה מפיק, ולכן
0הוא שקר, כל ספירה חיובית היא אמת.
my @keep = grep { $_ > 0 } @nums; # list context: filtered list
my $count = grep { $_ > 0 } @nums; # scalar context: count
מצב גלובלי שהוא נוגע בו#
$_משויך ככינוי לכל אלמנט שלLISTלמשך הערכה אחת שלBLOCKאוEXPR. ביציאה מ־grep,$_משוחזר לכל מה שהחזיק לפני הקריאה. מאחר שה־alias הוא לאלמנט האמיתי - לא לעותק - השמה ל־$_בתוך הבלוק משנה את רשימת המקור במקום. זהו אותו חוזה כמוmapוכמו משתנה האינדקס של לולאתfor.צורת ה־regex החשוף של
EXPRקוראת וכותבת את כל המשתנים הקסומים הקשורים להתאמה הרגילים ($1..$9,$&,${^MATCH},@+,@-,%+,%-,pos) בכל איטרציה, בדיוק כאילו כתבתם את ההתאמה בצורה מלאה.
דוגמאות#
סינון ערכים לא־מוגדרים:
my @vals = (1, undef, 2, undef, 3);
my @defined = grep { defined $_ } @vals;
# @defined = (1, 2, 3)
סינון regex דרך צורת הביטוי - ה־regex החשוף מותאם מול $_:
my @lines = ("# comment", "code", "# another", "more code");
my @code = grep !/^#/, @lines; # drop comments
my @comments = grep /^#/, @lines; # keep comments
בדיקה מותאמת אישית דרך צורת הבלוק, כאשר התנאי הוא יותר מביטוי אחד:
my @users = (
{ name => "alice", active => 1, age => 30 },
{ name => "bob", active => 0, age => 25 },
{ name => "carol", active => 1, age => 17 },
);
my @adults_active = grep {
$_->{active} && $_->{age} >= 18
} @users;
ספירה ללא בניית הרשימה - השתמשו בהקשר סקלר:
my @errors = ("ok", "fail", "ok", "fail", "fail");
my $nfail = grep { $_ eq "fail" } @errors; # 3
if (grep { $_ eq "fail" } @errors) { # boolean use
warn "had failures";
}
הסרת שורות ריקות ושורות עם רווח לבן בלבד:
my @clean = grep { /\S/ } @lines;
סינון מפתחות של האש לפי תכונה כלשהי של הערך:
my %scores = (alice => 42, bob => 17, carol => 95);
my @passing = grep { $scores{$_} >= 50 } keys %scores;
מקרי קצה#
$_הוא alias, לא עותק. שינוי$_בתוך הבלוק משנה את אלמנט הרשימה המקורי. זה לעתים שימושי אך לעתים קרובות יותר באג; כתבו בדיקה לקריאה בלבד:my @bumped = grep { $_++; $_ > 10 } @nums; # MUTATES @nums
אם האלמנטים עצמם אינם משתנים (לדוגמה ליטרלים, קבועים, או תוצאה של קריאת פונקציה), השמה ל־
$_היא שגיאת זמן ריצה - ״Modification of a read-only value״.אלמנטים מוחזרים הם aliases גם כן. אלמנטים של הרשימה המוחזרת חולקים אחסון עם רשימת המקור, בדיוק כמו משתנה האינדקס של לולאת
for. כתיבה אליהם דרך הרשימה המוחזרת מתפשטת בחזרה:my @arr = (1, 2, 3, 4); my @even = grep { $_ % 2 == 0 } @arr; $even[0] = 99; # @arr is now (1, 99, 3, 4)
זה רצוי לעיתים נדירות. העתיקו במפורש עם
map { $_ } grep ...אוmyרענן כאשר אתם צריכים אחסון עצמאי.צורת ביטוי לעומת צורת בלוק - קדימות.
grep EXPR, LISTמשתמש בפסיק כדי להפריד את הבדיקה מהרשימה;grep BLOCK LISTלא. ערבוב ביניהם מפיל אתכם:grep /x/, @arr; # EXPR form, correct grep { /x/ } @arr; # BLOCK form, correct grep { /x/ }, @arr; # WRONG: comma after block is a syntax # error or silently wrong depending on # context
grepבביטויים משורשרים.grepלוקחLIST, מה שאומר שכל מה שאחרי הארגומנט הראשון נצרך כחלק מהרשימה. השתמשו בסוגריים כאשר אתם רוצים לעקוב אחריgrepבאיברים נוספים:my @out = (grep { /x/ } @a), @b; # concatenates filtered @a and @b my @bad = grep { /x/ } @a, @b; # filters BOTH @a and @b together
לא לולאה.
grepהוא ביטוי, לא מבנה בקרה.last,next, ו־redoבתוך הבלוק מכוונים ללולאה העוטפת הקרובה ביותר, לא ל־grepעצמו - הם ייצאו או יתחילו מחדש את ה־for/whileשבתוכוgrepיושב, לא את ה־grep. השתמשו ב־returnמוקדם מתת־שגרה עוזרת אם אתם צריכים לקצר, או עברו ל־firstמ־List::Utilכאשר כל מה שאתם רוצים הוא ההתאמה הראשונה.קלט ריק הוא פלט ריק.
grepעל רשימה ריקה הוא רשימה ריקה בהקשר רשימה ו־0בהקשר סקלר. לא נדרש מקרה מיוחד.שיטוח האש.
grep { ... } %hעובר על שיטוח מפתח/ערך לסירוגין של ההאש; הבלוק רואה מפתחות וערכים משולבים ב־$_. זה כמעט אף פעם לא מה שאתם רוצים; סננו את המפתחות ובנו מחדש במקום זאת:my %filtered = map { $_ => $h{$_} } grep { some_test($_, $h{$_}) } keys %h;
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
map- אותו חוזה איטרציה עם אותה סמנטיקת alias של$_, אך מחזיר את מה שהבלוק הפיק עבור כל אלמנט במקום את האלמנט עצמוsort- סידור מחדש של רשימה; לעיתים קרובות משורשר עםgrepכ־sort { ... } grep { ... } @listany/all- בדיקות בוליאניות מקצרות על רשימה מ־List::Util; השתמשו באלה במקוםscalar grepכאשר אתם צריכים רק תשובת כן/לא והרשימה גדולה או הבדיקה יקרהfirst(מ־List::Util) - מחזיר את האלמנט הראשון התואם לבלוק ועוצר; הכלי הנכון כאשרgrepמשמש רק לאחזור התאמה אחתfor/foreach- מבנה הלולאה עם אותו כלל aliasing של$_; השתמשו בו כאשר אתם רוצים תופעות לוואי, לא רשימה מסוננת$_- הסקלר ברירת המחדל ש־grepמשייך ככינוי בכל איטרציה