von
Kai um
18:05 am
Montag, 28. Juni 2010 in
Programmiersprachen, Studium |
2 Kommentare
Fremden Code zu lesen ist grausam. Man muss sich nicht nur in eine völlig andere Denkweise hinein versetzen, sondern versuchen einzelne Stückchen zu einem großen Ganzen zusammenzusetzen.
Eigenen Code zu lesen ist aber mindestens genauso grausam, wenn der vorliegende Quelltext sagen wir älter als zwei Monate ist. Man vergisst schnell, was man sich beim entwickeln gedacht hat und warum man das entstehende Programm in eine bestimmte Richtung und nicht anders programmiert hat.
Wenn ich nun also zum Beispiel ein geschriebenes Programm aus dem ersten Semester um eine bestimmte Funktionalität erweitern müsste, dann wäre ich wahrscheinlich schneller, wenn ich das komplette Programm “from-scratch” noch einmal komplett neu schreiben würde.
Diese Erkenntnis ist natürlich schon lange bekannt und es gibt daher auch viele Konzepte, wie man diesem Problem entgegnen kann. Auf diese möchte ich hier aber gar nicht weitergehen, sondern versuchsweise aufzeigen, wie man im Kleinen schon darauf achten kann, lesbaren Code zu produzieren.
Als Beispielsprache verwende ich Python, weil mir erstens die Sprache sehr gut liegt und weil sie den Benutzer schon konzeptuell zwingt “saubereren” Code zu schreiben.
Kommentare sind dazu da, den eigenen Code zu erläutern bzw. zu erklären und verwendete Parameter auf ihre Bedeutung hin zu verdeutlichen. Im Prinzip wohl eine gute Sache. Ich finde den Einsatz von Kommentaren dennoch nicht gut, weil sie meiner Meinung nach das Problem nicht an der Wurzel packen: Wenn ich Code erst erklären muss, bevor sie einem dritten verständlich erscheinen, ist die Wahrscheinlichkeit hoch, dass entweder das zu lösende (Teil-)problem noch nicht ganz geknackt ist, oder meine Bezeichnungen für Funktionen und Variablen Mumpitz sind. Selbst Bezeichnungen für Hilfsvariablen sollten eindeutig bestimmen, wofür diese gebraucht werden.
Als Beispiel soll folgendes Szenario dienen:
Hier soll die Eingabe des Benutzer verarbeitet und an eine Funktion weitergegeben werden. Im ersten Beispiel ohne genauere Variablenbezeichnung und mit Kommentar, im zweiten Beispiel mit treffenden Bezeichnungen.
Obwohl die Lebensdauer der Variablen mindestgebot, auktionsdauer, dateipfad gerade mal zwei Zeilen beträgt, tragen diese doch erheblich zur Lesbarkeit bei:
# Eingabe: versteigere 50,60,/tmp/bild.jpg
eingabe = raw_input()
if eingabe.split()[0] == 'versteigere':
data = eingabe.split()[1].split(',')
if len(data) != 3:
print "error: wrong input"
else:
# Startet neue Auktion mit Mindestgebot,
# Dauer der Auktion und Pfad zum Bild
auktion.versteigere(data[0],data[1],data[2])
Und jetzt nochmal mit eindeutigen Bezeichnungen
eingabe = raw_input()
userBefehl = eingabe.split()[0]
if userBefehl == 'versteigere':
befehlsParameter = eingabe.split()[1].split(',')
if len(befehlsParameter) != 3:
print "error: wrong input"
else:
mindestgebot, auktionsdauer, dateipfad = befehlsParameter[:]
auktion.versteigere(mindestgebot, auktionsdauer, dateipfad)
Wie man sieht, hat man also nur durch eine bessere Bezeichnung der übergebenen Variablen ein Kommentar gespart und (hoffentlich) erheblich zur Lesbarkeit beigetragen.
Meines Erachtens darf hier auch ruhig redundante Information im Code vorkommen, wenn sie der Lesbarkeit dient. Im obigen Code ist das zum Beispiel das unnötige Slicing (befehlsParameter[:]), weil Python schlau genug ist, auch ohne das Slicing zu erkennen, dass die Liste mit drei Parameter auf die drei vorstehenden Variablen übergeben werden soll. Dennoch wird man durch das ‘[:]‘ beim Lesen noch einmal explizit darauf hingewiesen.
von
Kai um
20:37 am
Montag, 11. Januar 2010 in
Klausuren, Programmiersprachen, Studium |
0 Kommentare
Heute war es wieder mal soweit: Onlinetest. Dieses mal in Python. Nachdem ich erfolgreich meine Maschine (iMac mit Debian, Windowmanager war glaub ich FVWM) zweimal abgeschossen hatte, bin ich in der vorgegebenen Zeit von 90 Minuten knapp fertig geworden. Eigentlich sind die gestellten Aufgaben in den bisherigen Onlinetests, die ich so mitgeschrieben habe, nicht extrem schwer, trotzdem finde ich es aber schwierig auf Kommando kreativen (und möglichst cleveren) Code zu schreiben.
Es sind eben diese typischen Prüfungssituationen bei denen man unter erschwerten Bedingungen klaren Kopf behalten muss 
Das waren die Aufgaben:
Aufgabe 1:
Man soll von der Standardeingabe eine Zeile einlesen und unnötige Leerzeichen entfernen. Außerdem soll jedes Wort mit einem großen Anfangsbuchstaben in Großbuchstaben umgewandelt wieder ausgegeben werden.
# a1.py
while True:
line = raw_input()
line = line.split()
for word in line:
if(str.isupper(word[0])):
print str.upper(word),
else:
print word,
print # Zeilenumbruch für schönere Ausgabe
Aufgabe 2:
Die zweite Aufgabe bestand darin, eine Datei belegung.dat einzulesen, die folgendes Format hatte: <VL-Nr>; <matrikelNr>; <VL-Name>; <VL-Typ>.
Danach sollte die so eingelesene Datei in folgendem Format in eine Datei ausgabe.dat geschrieben werden:
<VL-Nr>,<AnzahlBelegungen>, <VL-Name> <VL-Typ>
Momentan sind in der Ausgabe noch doppelte Einträge drin, das müsste noch geändert werden.
# a2.py
dict = {}
belegung = []
input = open('belegung.dat','r')
for line in input:
line = line.split(';')
belegung.append(line)
input.close()
for line in belegung:
if dict.has_key(line[0]):
dict[line[0]] += 1
else:
dict[line[0]] = 1
ausgabe = file('ausgabe.dat','w')
for nummer, anzahl in dict.iteritems():
for line in belegung:
tmp = ""
if nummer==line[0]:
try:
tmp = str(nummer) + ',' + str(anzahl) + ',' + line[2] + '\n'
ausgabe.write(tmp)
except:
pass
ausgabe.close()
Aufgabe 3:
Die dritte Aufgabe bestand darin, einen “sprach-begabten” Taschenrechner zu programmieren, der beim Aufruf des Programms Argumente wie “17 plus 4 minus 3 gleich” übernimmt und korrekt auswertet. Es soll bewusst auf Punkt-vor-Strich Rechnung verzichtet werden (also fällt eval() flach). Außerdem kann man der Einfachheit davon ausgehen, dass nur korrekte Argumente übergeben werden.
Ich hab ungefähr so etwas hingeschrieben:
# a3.py
import sys
i = 0
result = 0
input = sys.argv[1:]
while i < len(input):
if input[1]=="gleich":
result = int(input[0])
break
elif input[i]=="plus":
result = int(input[i-1]) + int(input[i+1])
input[i+1] = result
elif input[i]=="minus":
result = int(input[i-1]) - int(input[i+1])
input[i+1] = result
elif input[i]=="mal":
result = int(input[i-1]) * int(input[i+1])
input[i+1] = result
elif input[i]=="durch":
result = int(input[i-1]) / int(input[i+1])
input[i+1] = result
i += 1
print "Ergebnis:",str(result)
von
Kai um
19:19 am
Montag, 23. November 2009 in
Programmiersprachen, Studium |
0 Kommentare
Heute fand nach sechswöchiger Einführung in C in dem Modul “Programmieren 3″ der Onlinetest in C statt. Nächste Woche gehts dann mit einer Einführung in Python weiter.
Da ich mir mittlerweile meiner akuten Prüfungsangst und der damit einhergehenden Blackouteritis bewusst bin, gehe ich davon aus, dass ich nur knapp bestanden hab, obwohl (zumindest die ersten beiden Aufgaben) nicht so schwer waren.
Nachfolgend nun die ersten beiden Aufgaben des Onlinetests. Die dritte Aufgabe reiche ich wie schonmal nach, sobald mein Kopf nicht mehr ganz so matschig ist.
Aufgabe 1
/****************************************************
* Aufgabe: Es werden Noten (Note '1' bis Note '6') *
* auf der Kommandozeile eingegeben. Als Ausgabe *
* erscheint dann die Summe der einzeln eingegeben- *
* en Noten *
****************************************************/
#include
int main(void) {
int n1=0,n2=0,n3=0,n4=0,n5=0,n6=0;
int note;
while(scanf("%d", ¬e) != EOF) {
switch (note) {
case 1:
n1++;
break;
case 2:
n2++;
break;
case 3:
n3++;
break;
case 4:
n4++;
break;
case 5:
n5++;
break;
case 6:
n6++;
break;
}
}
printf("Note 1: %d mal\n", n1);
printf("Note 2: %d mal\n", n2);
printf("Note 3: %d mal\n", n3);
printf("Note 4: %d mal\n", n4);
printf("Note 5: %d mal\n", n5);
printf("Note 6: %d mal\n", n6);
}
Aufgabe 2
/****************************************************
* Aufgabe: Versehentlich wurden einige Lottozahlen *
* doppelt gezogen. Es soll eine beliebige Reihe *
* von Zahlen auf der Kommandozeile eingegeben und *
* die doppelt vorkommenden Zahlen geklammert *
* werden *
****************************************************/
#include
#include
typedef struct zahlen {
int zahl;
struct zahlen *next;
} Zahl;
Zahl *neueZahl(void) {
Zahl *z;
z = malloc(sizeof(Zahl));
assert(z);
z->zahl = 0;
z->next = NULL;
return z;
}
Zahl *start;
Zahl *tmp;
int main(void) {
int eingabe;
int erstesMal = 0;
while(scanf("%d", &eingabe) != EOF) {
if(erstesMal == 0) {
Zahl *neu = neueZahl();
neu->zahl = eingabe;
neu->next = NULL;
start = neu;
tmp = neu;
erstesMal = 1;
} else {
Zahl *neu = neueZahl();
neu->zahl = eingabe;
tmp->next = neu;
tmp = neu;
}
}
printf("%d ",start->zahl);
tmp = start->next;
while(tmp != NULL) {
if(start->zahl == tmp->zahl) {
printf("[%d] ",tmp->zahl);
} else {
printf("%d ",tmp->zahl);
}
start = tmp;
tmp = start->next;
}
}
von
Kai um
11:57 am
Samstag, 15. August 2009 in
How-To, Programmiersprachen |
2 Kommentare
Semesterferien. Das bedeutet, (Frei)zeit, sich mit Sachen zu beschäftigen, für die sonst eher weniger Zeit übrig bleibt.
Ich wollte mir schon seit längerem mal Perl etwas näher anschauen, weil ich immer wieder gelesen habe, dass sie “programmiererfreundlich” sein soll, und man mit Perl “mal eben” ein Programm runter hacken kann. Im Prinzip also, ideale Vorraussetzungen für eine neue Sprache.
Nachdem ich mich nun einige Zeit damit beschäftigt habe, kann ich dem nur zustimmen. Die Sprache bietet die Möglichkeit, ein Problem auf so vielfältige Art und Weise zu lösen, dass man meist direkt anfangen kann etwas zu programmieren, ohne sich vorher darüber Gedanken zu machen, wie man das jetzt genau umsetzen muss.
Perl ist nicht sehr penibel, was die Syntax angeht. Das heißt konkret, dass ich es bisher nicht geschafft habe, ein Programm zu schreiben, was vom Compiler nicht auch übersetzt und ausgeführt wurde. Das ist schön, wenn das Programm danach auch genau das tut, was man erwartet hat. Wenn..
Es gibt viele Kleinigkeiten, die das Programmieren mit Perl einfacher machen. So wird in Funktionen stehts der letzte Ausdruck als Rückgabewert übergeben. Nicht initialisierte Arrays, Variablen, Listen etc. haben immer den Wert undef. Man braucht keine Hilfsvariablen, wenn man durch eine Liste oder ein Array durchiteriert usw. aber genau da liegt meines Erachtens auch der Hund begraben:
Man kann Programme schreiben wie Sau und alles ist gut:-) Ich denke, die Freiheit, die Perl einem bietet ist sowohl Segen als auch Fluch. Auf der einen Seite muss man sich weniger um sprachliche Barrieren kümmern (habe ich schon erwähnt, dass der Erfinder Larry Wall studierter Linguist ist?) und hat mehr Zeit für das eigentliche Problem, auf der anderen Seite muss man sich sehr disziplinieren und strukturiert vorgehen, sofern man den Quellcode in einem halben Jahr noch verstehen möchte. Wahrscheinlich ist Perl nicht gerade eine Sprache, die man Anfängern empfiehlt und ich schätze man sieht am Quellcode immer sehr schnell, aus was für einem Umfeld der Programmierer kommt, der das geschrieben hat. Ich habe mir nämlich sehr schnell angewöhnt, bei der mir vertrauten Java-Syntax zu bleiben, um kleinere Probleme möglichst direkt zu erschlagen.
Ein wirklich außergewöhnlich schönes Beispiel für die eben beschriebene Problematik bot der bis zum Jahr 2000 jährlich stattfindende “Perl Obfuscated Contest”:
@P=split//,".URRUU\c8R";@d=split//,"\nrekcah xinU / lreP rehtona tsuJ";sub p{
@p{"r$p","u$p"}=(P,P);pipe"r$p","u$p";++$p;($q*=2)+=$f=!fork;map{$P=$P[$f^ord
($p{$_})&6];$p{$_}=/ ^$P/ix?$P:close$_}keys%p}p;p;p;p;p;map{$p{$_}=~/^[P.]/&&
close$_}%p;wait until$?;map{/^r/&&<$_>}%p;$_=$d[$q];sleep rand(2)if/\S/;print
Wie dem geübten Leser wahrscheinlich sofort auffällt, gibt der Quellcode die Zeile “Just another Perl / Unix hacker” aus:-)
Mein persönliches Fazit also: Schöne Sprache, Freiheit ist nicht alles und immer schön diszipliniert bleiben.