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.
Τι επιστρέφεται#
Περιβάλλον λίστας: η φιλτραρισμένη λίστα, στην αρχική σειρά, με κάθε επιστρεφόμενο στοιχείο να είναι ψευδώνυμο μέσα στη λίστα-πηγή (δείτε Καθολική κατάσταση και Οριακές περιπτώσεις).
Βαθμωτό περιβάλλον: ο μετρητής των στοιχείων για τα οποία το τεστ ήταν αληθές. Όχι λογική τιμή - είναι το μέγεθος αυτού που θα παρήγαγε το περιβάλλον λίστας, οπότε το
0είναι ψευδές, οποιοσδήποτε θετικός μετρητής είναι αληθής.
my @keep = grep { $_ > 0 } @nums; # list context: filtered list
my $count = grep { $_ > 0 } @nums; # scalar context: count
Καθολική κατάσταση που επηρεάζει#
Η
$_είναι ψευδωνυμοποιημένη σε κάθε στοιχείο τηςLISTγια τη διάρκεια μιας αποτίμησης τουBLOCKή τηςEXPR. Κατά την έξοδο από τηgrep, η$_επαναφέρεται σε ό,τι κρατούσε πριν την κλήση. Επειδή το ψευδώνυμο είναι προς το πραγματικό στοιχείο - όχι αντίγραφο - η ανάθεση στην$_μέσα στο μπλοκ μεταβάλλει τη λίστα-πηγή επιτόπια. Αυτό είναι το ίδιο συμβόλαιο με την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;
Οριακές περιπτώσεις#
Η
$_είναι ψευδώνυμο, όχι αντίγραφο. Η τροποποίηση της$_μέσα στο μπλοκ τροποποιεί το αρχικό στοιχείο της λίστας. Αυτό περιστασιακά είναι χρήσιμο αλλά συχνότερα είναι σφάλμα· γράψτε τεστ μόνο για ανάγνωση:my @bumped = grep { $_++; $_ > 10 } @nums; # MUTATES @nums
Αν τα στοιχεία δεν είναι τα ίδια μεταβλητές (π.χ. κυριολεκτικές τιμές, σταθερές ή αποτέλεσμα κλήσης συνάρτησης), η ανάθεση στην
$_είναι σφάλμα χρόνου εκτέλεσης - «Modification of a read-only value».Τα επιστρεφόμενα στοιχεία είναι επίσης ψευδώνυμα. Τα στοιχεία της επιστρεφόμενης λίστας μοιράζονται αποθήκευση με τη λίστα-πηγή, ακριβώς όπως η μεταβλητή ευρετηρίου ενός βρόχου
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από μια βοηθητική sub αν χρειάζεται να βραχυκυκλώσετε, ή στρέψτε σεfirstτηςList::Utilόταν το μόνο που θέλετε είναι η πρώτη αντιστοιχία.Κενή είσοδος σημαίνει κενή έξοδος. Η
grepπάνω σε κενή λίστα είναι κενή λίστα σε περιβάλλον λίστας και0σε βαθμωτό περιβάλλον. Καμία ειδική περίπτωση δεν χρειάζεται.Ισοπέδωση πίνακα κατακερματισμού. Η
grep { ... } %hδιασχίζει την εναλλασσόμενη ισοπέδωση κλειδιού/τιμής του πίνακα κατακερματισμού· το μπλοκ βλέπει κλειδιά και τιμές μπλεγμένα στην$_. Αυτό σχεδόν ποτέ δεν είναι αυτό που θέλετε· φιλτράρετε τα κλειδιά και ξαναχτίστε αντ” αυτού:my %filtered = map { $_ => $h{$_} } grep { some_test($_, $h{$_}) } keys %h;
Διαφορές από το upstream#
Πλήρως συμβατό με το upstream Perl 5.42.
Δείτε επίσης#
map- ίδιο συμβόλαιο επανάληψης με την ίδια σημασιολογία ψευδώνυμου της$_, αλλά επιστρέφει αυτό που παρήγαγε το μπλοκ για κάθε στοιχείο αντί για το ίδιο το στοιχείοsort- αναδιάταξη λίστας· συχνά αλυσιδώνεται με τηgrepωςsort { ... } grep { ... } @listany/all- λογικά τεστ με βραχυκύκλωμα πάνω σε λίστα από τηνList::Util· χρησιμοποιήστε αυτά αντί γιαscalar grepόταν χρειάζεστε μόνο απάντηση ναι/όχι και η λίστα είναι μεγάλη ή το τεστ είναι δαπανηρόfirst(από τηνList::Util) - επιστρέφει το πρώτο στοιχείο που ταιριάζει με μπλοκ και σταματάει· το σωστό εργαλείο όταν ηgrepχρησιμοποιείται μόνο για να ληφθεί μία αντιστοιχίαfor/foreach- η κατασκευή βρόχου με τον ίδιο κανόνα ψευδωνυμοποίησης της$_· καταφύγετε σε αυτήν όταν θέλετε παρενέργειες, όχι φιλτραρισμένη λίστα$_- το προεπιλεγμένο βαθμωτό που ηgrepψευδωνυμοποιεί σε κάθε επανάληψη