Λίστες

map#

Εφαρμόζει ένα μπλοκ ή μια έκφραση σε κάθε στοιχείο μιας λίστας και επιστρέφει τα ισοπεδωμένα αποτελέσματα.

Η map αποτιμά το BLOCK (ή την EXPR) μία φορά ανά στοιχείο της LIST, με το $_ ψευδωνυμοποιημένο προς εκείνο το στοιχείο, και συλλέγει ό,τι επιστρέφει κάθε αποτίμηση σε μία μοναδική επίπεδη λίστα. Επειδή κάθε αποτίμηση μπορεί να αποδώσει μηδέν, μία ή πολλές τιμές, το μήκος της εξόδου είναι ανεξάρτητο από το μήκος της εισόδου - αυτή η ισοπέδωση είναι το νόημα της map. Σε περιβάλλον λίστας η map επιστρέφει τη γενόμενη λίστα· σε βαθμωτό περιβάλλον επιστρέφει τη συνολική καταμέτρηση στοιχείων.

Σύνοψη#

my @out = map { BLOCK } LIST;       # block form
my @out = map EXPR, LIST;           # expression form
my @out = map { f($_) } grep { ... } sort LIST;   # chained

Τι επιστρέφεται#

Μία μοναδική επίπεδη λίστα που κατασκευάζεται με συνένωση των τιμών επιστροφής κάθε αποτίμησης, στη σειρά εισόδου:

  • Επιστροφή μίας τιμής ανά είσοδο → μετασχηματισμός (ίδιο μήκος εξόδου).

  • Επιστροφή λίστας ανά είσοδο → επέκταση 1-προς-N (μεγαλύτερη έξοδος).

  • Επιστροφή της κενής λίστας () → παράλειψη αυτής της εισόδου (μικρότερη έξοδος· ιδίωμα map-ως-φίλτρο).

Σε βαθμωτό περιβάλλον η τιμή επιστροφής είναι ο αριθμός των γενόμενων στοιχείων, όχι αναφορά και όχι λογική τιμή. Η εκχώρηση του αποτελέσματος σε κατακερματισμό είναι καλά ορισμένη: η επίπεδη λίστα καταναλώνεται ως εναλλασσόμενα ζεύγη κλειδιού/τιμής.

my @doubled = map { $_ * 2 }          1 .. 4;     # (2, 4, 6, 8)
my @words   = map { split /\s+/, $_ } @lines;     # 1-to-N
my @kept    = map { $_ > 0 ? $_ : () } @nums;     # filter
my $count   = map { $_ * $_ } @nums;              # scalar ctx

Καθολική κατάσταση#

Το $_ ψευδωνυμοποιείται προς κάθε στοιχείο της LIST, δεν αντιγράφεται. Η τροποποίηση του $_ μέσα στο μπλοκ τροποποιεί το πηγαίο στοιχείο. Αυτό είναι περιστασιακά χρήσιμο αλλά συνήθως παγίδα - επιλέξτε έναν απλό βρόχο foreach όταν εννοείτε επιτόπια τροποποίηση.

my @v = (1, 2, 3);
my @r = map { $_ *= 10; $_ } @v;
# @v is now (10, 20, 30) - the source was mutated

Η ψευδωνυμοποίηση προς μη μεταβλητή (κυριολεκτικό, τιμή επιστροφής συνάρτησης, στοιχείο τομής κατακερματισμού που δεν υπάρχει ακόμη) και έπειτα εκχώρηση στο $_ αποτυγχάνει κατά τον χρόνο εκτέλεσης με Modification of a read-only value. Δείτε το $_.

Η map επίσης τοπικοποιεί το $_ γύρω από την κλήση, οπότε ένα εξωτερικό $_ ορατό στον καλούντα αποκαθίσταται όταν η map επιστρέψει.

Παραδείγματα#

Μετασχηματισμός - μετατροπή κάθε λέξης σε κεφαλαία:

my @upper = map { uc } @words;

Επέκταση 1-προς-N - διαχωρισμός κάθε γραμμής στους λευκούς χαρακτήρες στα tokens της:

my @tokens = map { split /\s+/ } @lines;

Ιδίωμα φίλτρου - διατηρήστε μόνο τις αληθείς, μετασχηματισμένες τιμές:

my @positives = map { $_ > 0 ? $_ : () } @numbers;

Κατασκευή κατακερματισμού από λίστα - κάθε αποτίμηση επιστρέφει λίστα δύο στοιχείων:

my %seen = map { $_ => 1 } @keys;
my %idx  = map { ($keys[$_] => $_) } 0 .. $#keys;

Συντεθειμένη διοχέτευση - ταξινόμηση, φιλτράρισμα, μετασχηματισμός. Οι map, grep και sort δέχονται όλες μια LIST στα δεξιά, οπότε αλυσιδώνονται από δεξιά προς τα αριστερά:

my @top = map  { "$_->{name}: $_->{score}" }
          sort { $b->{score} <=> $a->{score} }
          grep { $_->{score} >= 50 }
          @records;

Ισοπέδωση δομής - κάθε είσοδος συνεισφέρει μεταβλητό αριθμό εξόδων:

my @leaves = map { ref($_) eq 'ARRAY' ? @$_ : $_ } @mixed;

Ζευγαρώστε με δείκτη χρησιμοποιώντας το List::Util::pairs ή ρητή μεταβλητή δείκτη - η ίδια η map δεν εκθέτει τέτοια:

my @numbered = do {
    my $i = 0;
    map { sprintf "%d. %s", ++$i, $_ } @items;
};

Οριακές περιπτώσεις#

  • Το $_ ψευδωνυμοποιείται, δεν αντιγράφεται. Η εκχώρηση στο $_ γράφει πίσω στην πηγή. Αν το πηγαίο στοιχείο είναι μόνο για ανάγνωση (σταθερά, τιμή επιστροφής κλήσης συνάρτησης ισοπεδωμένη στη LIST), η εκχώρηση εγείρει Modification of a read-only value. Αντιμετωπίστε το $_ ως μόνο εισόδου εκτός αν θέλετε συγκεκριμένα την παρενέργεια.

  • Η map δεν είναι βρόχος. Οι last, next, redo και οι ετικέτες βρόχου δεν εφαρμόζονται στη map. Αναφέρονται στον περικλείοντα πραγματικό βρόχο και θα συμπεριφερθούν αναλόγως - συνήθως όχι αυτό που εννοούσε ο συγγραφέας. Χρησιμοποιήστε foreach όταν χρειάζεστε ροή ελέγχου βρόχου.

  • Το κενό περιβάλλον είναι παρωχημένο. Η map { ... } LIST; με απορριμμένο αποτέλεσμα εκπέμπει προειδοποίηση Useless use of map in void context υπό use warnings. Όταν θέλετε τις παρενέργειες, όχι τη λίστα, γράψτε foreach.

  • Αμφισημία αναλυτή BLOCK έναντι EXPR. Το { εκκινεί τόσο μπλοκ όσο και κατασκευαστή αναφοράς κατακερματισμού. Τα map { ... } LIST και map { ... }, LIST φαίνονται πανομοιότυπα στον αναλυτή στην ανοιγόμενη αγκύλη· η Perl μαντεύει βάσει του τι ακολουθεί. Ένα μπλοκ που ξεκινά με κάτι που μοιάζει με κλειδί κατακερματισμού συν => είναι ιδιαίτερα πιθανό να εικαστεί λάθος:

    my %h = map {  "\L$_" => 1  } @a;   # guessed EXPR, wrong
    my %h = map { +"\L$_" => 1  } @a;   # unary + forces BLOCK
    my %h = map {; "\L$_" => 1  } @a;   # leading ; forces BLOCK
    my %h = map { ($_, 1) }       @a;   # parens - unambiguous
    my %h = map { lc($_) => 1 }   @a;   # function call - unambiguous
    

    Για να επιστρέψετε ανώνυμο κατακερματισμό ανά στοιχείο, εξαναγκάστε τον κατασκευαστή αναφοράς κατακερματισμού με +{:

    my @hashes = map +{ lc($_) => 1 }, @array;
    
  • Η σειρά είναι σταθερή. Η map αποτιμά από αριστερά προς τα δεξιά και συνενώνει αποτελέσματα από αριστερά προς τα δεξιά. Η γενόμενη λίστα διατηρεί τη σειρά εισόδου, με τη συνεισφορά κάθε εισόδου να εμφανίζεται συνεχόμενα.

  • Κενή LIST. Η map αποτιμά το μπλοκ μηδέν φορές και επιστρέφει την κενή λίστα. Το μπλοκ δεν αποτιμάται για τις παρενέργειές του.

  • Εμφωλευμένη map. Η εσωτερική map βλέπει το δικό της $_· το εξωτερικό $_ επικαλύπτεται για όλη τη διάρκεια της εσωτερικής αποτίμησης. Αποτυπώστε πρώτα την εξωτερική τιμή αν χρειάζονται και οι δύο:

    my @pairs = map {
        my $outer = $_;
        map { [$outer, $_] } @inner
    } @outer;
    

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

Δείτε επίσης#

  • grep - ίδιου σχήματος με τη map, αλλά διατηρεί στοιχεία για τα οποία το μπλοκ είναι αληθές αντί να τα μετασχηματίζει· αλυσιδώνεται φυσικά με map και sort

  • sort - διατάσσει μια λίστα· συνήθως συντίθεται με map μέσω του Schwartzian Transform (mapsortmap)

  • for / foreach - χρησιμοποιήστε όταν θέλετε επανάληψη για παρενέργειες, τροποποίηση της πηγής, ή έλεγχο βρόχου (last/next/redo)

  • x - τελεστής επανάληψης λίστας· ορθογώνιος με τη map αλλά χρησιμοποιείται συχνά για να κατασκευάσει τη LIST που καταναλώνει η map

  • List::Util::reduce - αναδιπλώστε μια λίστα σε μία τιμή· χρησιμοποιήστε όταν το αποτέλεσμα δεν είναι λίστα αλλά συγκεντρωτική τιμή (άθροισμα, μέγιστο, πρώτο ταίριασμα)

  • $_ - το ψευδώνυμο στοιχείου μέσα στο μπλοκ· μόνο για ανάγνωση όταν το πηγαίο στοιχείο είναι κυριολεκτικό ή τιμή επιστροφής συνάρτησης