Sortieren
sort
Wie bereits bekannt sein sollte, werden Arrays mit Hilfe des sort-Befehls sortiert. Jedoch geschieht dies auf der Basis von alphanumerischer Sortierung; d. h. Zahlen werden wie Strings behandelt, was bewirkt dass 17 kleiner ist als 9.
@z=(1..20);
print join(", ", sort(@z));
1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 3, 4, 5, 6, 7, 8, 9
Sortieren von Zahlen
Was wäre Perl, wenn man obiges Problem nicht beseitigen könnte?
Die Lösung lautet ganz einfach: Man sagt Perl, wie es sortieren soll: Man schreibt nach dem sort-Befehl und vor dem zu sortierenden Array in geschweiften Klammern die Sortieranweisung:
@z=(1..20);
print join(", ", sort{$a<=>$b}(@z));
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
Das fällt auf:
- Es werden zwei Variablen $a und $b verwendet. Man benützt diese Variablen, um festzulegen wie zwei beliebige Elemente $a und $b aus dem Array verglichen werden.
- Es wird der <=>-Opereator benützt, um $a und $b zu vergleichen. Dieser liefert 1, falls die linke Zahl größer als die rechte ist, 0, falls beide gleich sind, und -1, falls die rechte Zahl größer als die linke ist.
Sortiervorschrift angeben
Perl vergleicht also $a mit $b und möchte als Ergebnis 1, 0 oder -1 erhalten. Falls 1 zurückgegeben wird, dann war $a größer, falls 0 zuurückgegeben wird, war $a und $b gleich, falls -1 zurückgegeben wird, war $b größer. Genauer gesagt interpretiert Perl die Rückgabewerte folgendermaßen: eine Zahl größer oder gleich 1 für größer, eine Zahl zwischen -1 und 1 (ausschließlich 1 und -1) für gleich und eine Zahl kleiner gleich -1 für kleiner.
Im obigen Abschnitt haben wir bereits eine Sortiervorschrift angegeben. Diese kann man beliebig gestalten - sie muss eben nur Zahlen wie 1, 0 oder -1 zurückgeben:
@worte=("Auto", "abnehmen", "ADAC", "Abfindung", "andauern");
print "normal: ".(join " - ", sort @worte)."\n";
print "UPPERCASE: ".(join " - ", sort{"\U$a" cmp "\U$b"}@worte)."\n";
print "3rd Letter: ".(join " - ", sort{substr($a,2,1) cmp substr($b,2,1)}@worte);
normal: ADAC - Abfindung - Auto - abnehmen - andauern
UPPERCASE: Abfindung - abnehmen - ADAC - andauern - Auto
3rd Letter: ADAC - andauern - Abfindung - abnehmen - Auto
- In der ersten sort-Anweisung wird keine Sortiervorschrift angegeben. Die Wörter werden ganz normal sortiert: nach dem ASCII-Zeichensatz. Dort ist A kleiner als a.
- Im zweiten Fall werden die Inhalte der Variablen $a und $b zuerst in Großbuchstaben umgewandelt, bevor diese verglichen werden. Damit werden Wörter, die z. B. mit Kleinbuchstaben beginnen, eingreiht.
- Im letzten Fall werden jeweils die dritten Buchstaben der Wörter verglichen und die Wörter danach sortiert.
Sortiervorschrift als Funktion angeben
Man kann die Sortiervorschrift auch als Funktion formulieren. Das sieht dann so aus:
@z=(1..20);
print join(", ", sort{&sortNumbers}(@z))."\n";
print join(", ", sort sortNumbers @z)."\n";
print join(", ", sort{&sortNumbers2}(@z))."\n";
sub sortNumbers() {
$a<=>$b;
}
sub sortNumbers2() {
if ($a < $b) { -1; }
elsif ($a==$b) { 0; }
else { 1; }
}
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
Statt den Code in die geschweiften Klammern zu schreiben, wird dieser in einer extra Funktion angegeben. Auch hier werden die vordefinierten Variablen $a und $b benutzt. Man kann sich so eine spezielle Funktion für Sortierung erstellen und sie immer wieder verwenden.
Sortieren der Zeilen einer Matrix
Im folgenden Beispiel soll eine Matrix nach Zeilen sortiert werden. Es wird davon ausgegangen, dass die Matrix nur Zahlen enthält und dass alle Zeilen gleich lang sind. Eine Zeile ist größer als die andere, falls es einen Eintrag in der einen Zeile gibt, der größer ist als der Eintrag an der gleichen Position der anderen Zeile. Vor dieser Position müssen alle gleich sein. Zwei Zeilen sind gleich, falls alle Einträge gleich sind. Eine Zeile ist kleiner als die andere Zeile, falls die andere Zeile größer ist als die eine.
@A=( [1, 2, 3, 4],
[5, 6, 7, 8],
[9, 0, 1, 2] );
#sortieren
@sortedA = sort{&arraySortDown}@A;
# Ausgabe
foreach (@sortedA) { print join(", ", @$_)."\n"; }
sub arraySortDown{
my $my_cmp=0;
for (my $i=0; $i<=$#$a; $i++) {
if ($my_cmp=$$b[$i]<=>$$a[$i]) { last; }
}
$my_cmp;
}
9, 0, 1, 2
5, 6, 7, 8
1, 2, 3, 4
Das fällt auf:
- Es wurde eine Sortierfunktion angewendet.
- Die Variablen $a und $b enthalten jeweils eine Referenz auf ein Array.
- In der for-Schleife werden die $i-ten Elemente des Arrays verglichen und das Ergebnis in $my_cmp gespeichert. Ist das Ergebnis ungleich 0, dann sind wir nach der Definition von größer und kleiner mit dem Vergleich fertig und können die Schleife und anschließend das Unterprogramm beenden. Dies geschieht durch den Befehl last, der aus der aktuellen Schleife springt.