Vermutlich ist das folgende alles kalter Kaffee und es steht in jedem zweiten Programmierhandbuch - aber ich habe die alle nicht gelesen und es deshalb nochmal ganz alleine neu und selbst erfunden.
Die Aufgabe war, ca. 71000 Datensätze aus einer Datei mit ca. 500 Datensätzen aus einer anderen Daten zu krossreferenzieren¹ und daraus eine passende Ausgabe zu erstellen. Mit einem kleinen handgeklöppelten Perlskript war das fix umgesetzt, Text- und Listenbearbeitung ist da ja quasi ein Heimspiel.
Als das Skript seine erste Ausgabe produziert hatte, stellte sich mir die Frage: Aha, soso, interessant - und was hat es nun genau gemacht? 210000 Zeilen Ausgabe möchte man nicht von Hand durchgucken. Also musste etwas Statistik und Tracing im Programm verteilt werden – und dabei kam mir eine kleine Erleuchtung, wie sich das in Perl wunderbar einfach und vielleicht sogar elegant umsetzen kann.
Man definiert sich einfach am Anfang des Skripts einen Hash:
my %count;
und gibt diesen am Ende des Scripts formatiert aus (und zwar so, dass er nicht mit der regulären Ausgabe kollidiert):
printf STDERR ("%10d %s\n", $counter{$_}, $_) foreach (sort keys %counter);
Fertig!
Der Trick ist, dass man vorher gar nicht angibt, was man zählen will: Dank Perls
autovivication kann man sich je nach Bedarf neue Zähler mitten im Programm aus dem Hin^H^Hut ziehen:
while (do_something)
{
if ($good)
{
...
$count{'ok'}++;
}
else
{
$count{'error'}++;
}
$count{'total'}++;
}
Dank der Autovivication führt das auch bei
use strict
und
use warnings
weder zu Fehlern noch zu Warnungen.
Die Ausgabe am Programmende findet dynamisch alle verwendeten Keys und gibt sie aus. Will man auch unbenutzte Keys anzeigen, muss man sie lediglich manuell initialisieren.
Weitere Schweinereien liegen schnell auf der Hand: Wenn man eine² initiale Warnung über "uninitialized value" verkraftet, kann man sich z.B. mit einer einzigen Zeile einen Maximalwert berechnen und ausgeben lassen:
while (do_something)
{
$count{'max'} = $value if $value > $count{'max'};
}
Auf Kosten einer zweiten Zeile am Programmstart bekommt man die Warnung auch noch weg - aber das grenzt dann ja schon an umständlich, da ist ja der Spaß des dynamischen Dazuerfindens von Zählern weg.
Und wenn man den numerischen Zählerwert ignoriert, kann man den Zählerschlüssel selbst für eine Textnachricht benutzen, um z.B. Warnungen ans Ende des Programms zu verschieben oder Debugging von Sonderkonstellationen einzubauen:
while (do_something)
{
if (unbelievable_event)
{
$count{"weird data in item $index: foo=$foo, baz=$baz"}++;
}
}
Feine Sache das, werde ich bestimmt öfter mal irgendwo reinfrickeln.
¹Duden hilf!
²nun ja, falls $value mit 0 oder weniger anfängt, auch mehrere