Λίστες

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

  • any / all - λογικά τεστ με βραχυκύκλωμα πάνω σε λίστα από την List::Util· χρησιμοποιήστε αυτά αντί για scalar grep όταν χρειάζεστε μόνο απάντηση ναι/όχι και η λίστα είναι μεγάλη ή το τεστ είναι δαπανηρό

  • first (από την List::Util) - επιστρέφει το πρώτο στοιχείο που ταιριάζει με μπλοκ και σταματάει· το σωστό εργαλείο όταν η grep χρησιμοποιείται μόνο για να ληφθεί μία αντιστοιχία

  • for / foreach - η κατασκευή βρόχου με τον ίδιο κανόνα ψευδωνυμοποίησης της $_· καταφύγετε σε αυτήν όταν θέλετε παρενέργειες, όχι φιλτραρισμένη λίστα

  • $_ - το προεπιλεγμένο βαθμωτό που η grep ψευδωνυμοποιεί σε κάθε επανάληψη