map und grep sind zwei sehr mächtige Perl-Befehle: Richtig eingesetzt ersparen sie so manche überflüssige Programmzeile und zusätzlichen Speicherbedarf. Mit Hilfe zweier unscheinbarer Klammern kann map um die Funktion von grep erweitert werden.
Schleifen gehören zu den Grundbausteinen fast aller Programmiersprachen, selbst ihre Schlüsselwörter sind fast immer gleich:
my @a = (10,1,3,42,5,2,666,9);
my @b;
for my $item (@a) { # Alle Elemente von @a durchgehen
if ($item < 10) { # Nur Werte kleiner als 10 bearbeiten
push @b, $item * 2; # Wert verdoppeln und an @b anhängen
}
}
Diese Schleife alle Einträge kleiner als 10 soll aus dem Array @a heraussuchen, verdoppeln und in @b ablegen.
Das gleiche Problem lässt sich auch mit Hilfe von map und grep lösen:
my @a = (10,1,3,42,5,2,666,9);
my @temp = grep { $_ < 10 } @a; # Alle Werte unter 10 heraussuchen
my @b = map { $_ * 2 } @temp; # Alle Werte verdoppeln
grep führt den Quellcode zwischen { und } für jeden Wert des Ursprungsarray @a aus. Der Wert des aktuellen Elements wird dabei in $_ übergeben. Ist das Ergebnis der Funktion wahr (true), wird der Ursprungswert in die Rückgabemenge übernommen, andernfalls einfach verworfen.
map arbeitet sehr ähnlich, allerdings wird in jedem Fall das Ergebnis der Funktion zurückgeliefert, auch wenn es unwahr (false) ist.
Diese ausführliche Variante nutzt noch ein temporäres Array als Zwischenspeicher. Werden map und grep in eine Zeile zusammengezogen, kann darauf aber verzichtet werden:
my @a = (10,1,3,42,5,2,666,9);
my @b = map { $_ * 2 } grep { $_ < 10 } @a;
Die zweite Zeile sollte von rechts nach links gelesen werden:
- Basismenge: Alle Elemente aus @a
- grep gibt nur die Werte kleiner 10 zurück
- map verdoppelt alle übrig gebliebenen Werte
Möglich ist auch eine Variante, die ganz auf grep verzichtet:
my @a = (10,1,3,42,5,2,666,9);
my @b = map { if ($_ < 10) { $_ * 2 } } @a;
In diesem Fall werden auch nur die Werte kleiner 10 verdoppelt, allerdings enthält @b jetzt den Wert undef an allen Stellen, an denen vorher Werte größer oder gleich 10 standen. map führt die angegebene Funktion für jeden Wert aus. Entweder ist die if-Bedingung erfüllt (dann wird verdoppelt) oder nicht - in diesem Fall ist das Funktionsergebnis der Rückgabewert von if und dieser ist undef als einer der möglichen Werte für unwahr (false).
Mit einem kleinen Trick lässt sich dieses Problem umgehen:
my @a = (10,1,3,42,5,2,666,9);
my @b = map { if ($_ < 10) { $_ * 2 } else { () } } @a;
Jetzt wird das Ergebnis der if-Bedingung unter keinen Umständen zurückgeliefert, weil für den Negativfall eine else-Funktion bereitsteht. Diese gibt eine leere Liste zurück, die keine Elemente enthält, die map für diesen Wert von @a zurückgeben könnte - folglich findet sich für diese Elemente auch kein Eintrag im Ergebnis-Array @b.
/p
1 Kommentar. Schreib was dazu-
Brad Gilbert
2.02.2014 5:13
Antworten
You could also write it as:
my @b = map{ ($_ * 2) x ($_