Es ist meine feste Überzeugung, dass alle Probleme, die zu lösen sich lohnt, schon gelöst worden sind oder zu kompliziert sind, um sie selbst zu lösen. CPAN ist ein gutes Argument für diese Überzeugung. Aber so, wie andere Leute unbedingt selbst ihr Brot backen wollen, will ich meine Probleme so weit wie möglich selbst lösen. Ist ja auch viel gesünder. Dass ich dabei auf Module zurückgreife, ist keine Inkonsequenz, sondern Dialektik.
Das folgende Skript hat jedenfalls eine lange Geschichte. Vor mittlerweile 25 Jahren wurde mir im Informatikunterricht die Aufgabe gestellt, einen Konverter zwischen römischen und arabischen Dezimalzahlen in Pascal zu schreiben. Damals habe ich mich unbändig über das nur sehr selten hilfreiche, aber funktionierende Ergebnis gefreut. Einige Jahre später hätte ich es plötzlich gebraucht – es war weg. Das hat mich so geärgert, dass ich nicht nur meine Backup-Strategie erneut verfeinern, sondern auch den Code in Perl replizieren musste.
Zwei Tage, nachdem das Skript zu meiner Zufriedenheit funktionierte, warf ich einen Blick ins Perl Cookbook. Es ist schön, wenn eine Überzeugung bestätigt wird: Sakuro Ozawa, der Autor des Moduls Roman
, hatte das Problem deutlich eleganter gelöst. Aber zu meiner großen Freude ist seine Lösung zumindest bei der Umwandlung von Dezimalzahlen in römische Zahlen 72% langsamer als mein Skript (Benchmark: 10 Millionen Umwandlungen von 1984). Ein Beleg dafür, dass Gewalt (in diesem Fall die brute force regex-Methode) manchmal doch eine Lösung ist. Umgekehrt (10 Millionen Umwandlungen von MMDCXLIX) läuft es weniger gut: Mr. Ozawa lässt mich mit 86% schlechterer Performance ganz alt aussehen. So ist das Leben.
#!/usr/bin/perl -w $zahl = <>; if ($zahl =~ m/[MDCLXVI]/){ while ($zahl =~ m/I(?!(V|X))/g){ $arabsum += 1 } while ($zahl =~ m/I(?=(V|X))/g){ $arabsum -= 1 } while ($zahl =~ m/V/g){ $arabsum += 5 } while ($zahl =~ m/X(?!(L|C))/g){ $arabsum += 10 } while ($zahl =~ m/X(?=(L|C))/g){ $arabsum -= 10 } while ($zahl =~ m/L/g){ $arabsum += 50 } while ($zahl =~ m/C(?!(D|M))/g){ $arabsum += 100 } while ($zahl =~ m/C(?=(D|M))/g){ $arabsum -= 100 } while ($zahl =~ m/D/g){ $arabsum += 500 } while ($zahl =~ m/M/g){ $arabsum += 1000 } print $arabsum; } else { @romarray = split //, $zahl; $romlength = scalar @romarray; if ($romlength >= 7) { print "Maximal sechsstellige Zahlen!"; } else { if ($romlength == 6) { $romsum .= "M" x ($romarray[-6] * 100); } if ($romlength >= 5) { $romsum .= "M" x ($romarray[-5] * 10); } if ($romlength >= 4) { $romsum .= "M" x $romarray[-4]; } if ($romlength >= 3) { $romsum .= "C" x $romarray[-3]; } if ($romlength >= 2) { $romsum .= "X" x $romarray[-2]; } if ($romlength >= 1) { $romsum .= "I" x $romarray[-1]; } $romsum =~ s/C{9}/CM/g; $romsum =~ s/C{5}/D/g; $romsum =~ s/C{4}/CD/g; $romsum =~ s/X{9}/XC/g; $romsum =~ s/X{5}/L/g; $romsum =~ s/X{4}/XL/g; $romsum =~ s/I{9}/IX/g; $romsum =~ s/I{5}/V/g; $romsum =~ s/I{4}/IV/g; print $romsum;} }