רשימות

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 { ... } @list

  • any / all - בדיקות בוליאניות מקצרות על רשימה מ־List::Util; השתמשו באלה במקום scalar grep כאשר אתם צריכים רק תשובת כן/לא והרשימה גדולה או הבדיקה יקרה

  • first (מ־List::Util) - מחזיר את האלמנט הראשון התואם לבלוק ועוצר; הכלי הנכון כאשר grep משמש רק לאחזור התאמה אחת

  • for / foreach - מבנה הלולאה עם אותו כלל aliasing של $_; השתמשו בו כאשר אתם רוצים תופעות לוואי, לא רשימה מסוננת

  • $_ - הסקלר ברירת המחדל ש־grep משייך ככינוי בכל איטרציה